随机算法 - HNU 13348 Finding Lines

2024-09-05 17:08

本文主要是介绍随机算法 - HNU 13348 Finding Lines,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 Finding Lines

Problem's Link:  http://acm.hnu.cn/online/?action=problem&type=show&id=13348&courseid=0


 

Mean: 

给你平面上1e5个点,让你判断是否可以找到一条直线,使得p%的点都在这条直线上。

analyse:

经典的随机算法题。

每次随机出两个点,然后对n个点进行判断,看是否有p%的点在这条直线上。

关于随机算法正确性的证明:

每次随机一个点,这个点在直线上的概率是p,p最小为20%;

随机两个点来确定一条直线,那么随机一条直线的概率就是p*p,即:4%;

也就是说,在直线上概率为0.04,不在的概率为1-0.04=0.96;

那么随机k次不在直线上的概率为0.96^k,也就是答案出现误差的概率。

这题我们随机1000次,错误的概率接近1e-18,基本是不可能事件,证毕.

Time complexity: O()

 

Source code: 

/*
* this code is made by crazyacking
* Verdict: Accepted
* Submission Date: 2015-08-02-17.06
* Time: 0MS
* Memory: 137KB
*/
#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <ctime>
#include <bits/stdc++.h>
#define  LL long long
#define  ULL unsigned long long
using namespace std;
const LL MAXN = 100010;

LL n , p;
LL x [ MAXN ], y [ MAXN ];
int main()
{
      srand( time( NULL ) );
      while( ~ scanf( "%lld %lld" , &n , &p ) )
      {
            for( LL i = 0; i < n; ++ i )
                  scanf( "%lld %lld" , & x [ i ], & y [ i ] );
            if( n == 1 ) { puts( "possible" ); continue; }
            LL sum = 0;
            if( n * p % 100 == 0 ) sum = n * p / 100;
            else sum = n * p / 100 + 1;
            LL T = 1000;
            bool flag = false;
            while( T -- )
            {
                  LL a = ( LL ) rand() * ( LL ) rand() % n;
                  LL b = ( LL ) rand() * ( LL ) rand() % n;
                  if( a == b ) continue;
                  LL num = 2;
                  for( LL j = 0; j < n; ++ j )
                  {
                        if( j == a || j == b ) continue;
                        if( ( y [ j ] - y [ a ] ) * ( x [b ] - x [ a ] ) == ( x [ j ] - x [ a ] ) * ( y [b ] - y [ a ] ) ) num ++;
                  }
                  if( num >= sum ) { flag = true; break ;}
            }
            if( flag ) puts( "possible" );
            else puts( "impossible" );
      }
      return 0;
}
/*

*/

 

 另外一种据说更高效的解法:

// Time complexity: O(n/(p/100)^2)
// Memory: O(n)

/* Solution method:
*
* Select 10000 (or more) pairs of points at random, and determine the lines that go through them.
* If 20% (or more) of all points are on the same line, we expect to find it about 400 times(*).
* For all lines that we find more than 230 times (at most 43), see how many points are on it.
* Report "possible" if one of them works.
* (*) Worst case: 15 points, p=20%, exactly 3 on one line: E = 10000/35 = 285.7, sigma = 16.7
*/

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <map>
using namespace std;

const int N = 1000000;
const int samples = 10000 , threshold = 230;

int X [N ], Y [N ];

int gcd ( int a , int b)
{     return b ? gcd(b , a %b) : a;
}

struct line
{     long long a , b , c; // ax+by=c
    line() {}
    // Construct line through (x1,y1) and (x2,y2)
    line ( int x1 , int y1 , int x2 , int y2)
    {     int d = gcd( abs( x1 - x2 ), abs( y1 - y2));
        if ( x1 - x2 < 0)
            d = - d;
        a = -( y1 - y2) / d;
       b = ( x1 - x2) / d;
        c = a * x1 + b * y1;
    }
};

bool operator < ( line L1 , line L2)
{     return L1 . a < L2 . a || ( L1 . a == L2 . a && ( L1 .b < L2 .b || ( L1 .b == L2 .b && L1 . c < L2 . c)));
}

map < line , int > Cnt;

// RNG (modulo 2^32)
unsigned int randnr()
{     static unsigned int rseed = 131;
    return rseed = 22695477 * rseed + 1;
}

int main()
{     int n , p , i , j , m , s;
    long long a , b , c;
    map < line , int >:: iterator it;
    // Read input
    scanf( "%d %d" , &n , &p);
    for ( i = 0; i < n; i ++)
        scanf( "%d %d" , & X [ i ], & Y [ i ]);
    // Special case: n=1
    if (n == 1)
    {     printf( "possible \n ");
        return 0;
    }
    // Try a lot of random pairs of points
    for (s = 0; s < samples; s ++)
    {
            i = randnr() % n;
        do
            j = randnr() % n;
        while ( j == i);
        Cnt [ line( X [ i ], Y [ i ], X [ j ], Y [ j ])] ++; // Add to count
    }
    // Check all lines whose count is above the threshold
    for ( it = Cnt . begin(); it != Cnt . end(); it ++)
        if ( it -> second > threshold)
        {     a = ( it -> first ). a;
           b = ( it -> first ).b;
            c = ( it -> first ). c;
            m = 0; // Count
            for ( i = 0; i < n; i ++)
                if ( a * X [ i ] + b * Y [ i ] == c)
                    m ++;
            if ( 100 * m >= n *p)
            {     printf( "possible \n ");
                return 0;
            }
        }
    printf( "impossible \n ");
    return 0;
}

 再附上一种二分的做法:

#include <cstdio>
#include <cmath>
#include <set>
using namespace std;

const int N = 1000000;

int X [N ], Y [N ];

int gcd ( int a , int b)
{     return b ? gcd(b , a %b) : a;
}

struct line
{     long long a , b , c; // ax+by=c
    line() {}
    // Construct line through (x1,y1) and (x2,y2)
    line ( int x1 , int y1 , int x2 , int y2)
    {     int d = gcd( abs( x1 - x2 ), abs( y1 - y2));
        if ( x1 - x2 < 0)
            d = - d;
        a = -( y1 - y2) / d;
       b = ( x1 - x2) / d;
        c = a * x1 + b * y1;
    }
};

bool operator < ( line L1 , line L2)
{     return L1 . a < L2 . a || ( L1 . a == L2 . a && ( L1 .b < L2 .b || ( L1 .b == L2 .b && L1 . c < L2 . c)));
}

set < line > findlines ( int first , int last , int p)
{     int mid = ( first + last) / 2;
    int a , b , c , i , j , m;
    set < line > S , S1;
    if (p *( mid - first) <= 100) // Too few points left to split
    {     for ( i = first; i < last; i ++)
            for ( j = i + 1; j < last; j ++)
                S1 . insert( line( X [ i ], Y [ i ], X [ j ], Y [ j ]));
    }
    else
    {     S1 = findlines( first , mid , p);
        set < line > S2 = findlines( mid , last , p);
        S1 . insert( S2 . begin (), S2 . end());
    }
    set < line >:: iterator it;
    for ( it = S1 . begin(); it != S1 . end(); it ++)
    {     a = it -> a;
       b = it ->b;
        c = it -> c;
        m = 0;
        for ( i = first; i < last; i ++)
            if ( a * X [ i ] + b * Y [ i ] == c)
                m ++;
        if ( 100 * m >= p *( last - first))
           S . insert( * it);
    }
    return S;
}

int main()
{     int n , p , i;
    scanf( "%d %d" , &n , &p);
    for ( i = 0; i < n; i ++)
        scanf( "%d %d" , & X [ i ], & Y [ i ]);
    printf( "%spossible \n " , n == 1 || ! findlines( 0 , n , p ). empty() ? "" : "im");
    return 0;
}

 

这篇关于随机算法 - HNU 13348 Finding Lines的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1139561

相关文章

不懂推荐算法也能设计推荐系统

本文以商业化应用推荐为例,告诉我们不懂推荐算法的产品,也能从产品侧出发, 设计出一款不错的推荐系统。 相信很多新手产品,看到算法二字,多是懵圈的。 什么排序算法、最短路径等都是相对传统的算法(注:传统是指科班出身的产品都会接触过)。但对于推荐算法,多数产品对着网上搜到的资源,都会无从下手。特别当某些推荐算法 和 “AI”扯上关系后,更是加大了理解的难度。 但,不了解推荐算法,就无法做推荐系

康拓展开(hash算法中会用到)

康拓展开是一个全排列到一个自然数的双射(也就是某个全排列与某个自然数一一对应) 公式: X=a[n]*(n-1)!+a[n-1]*(n-2)!+...+a[i]*(i-1)!+...+a[1]*0! 其中,a[i]为整数,并且0<=a[i]<i,1<=i<=n。(a[i]在不同应用中的含义不同); 典型应用: 计算当前排列在所有由小到大全排列中的顺序,也就是说求当前排列是第

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

综合安防管理平台LntonAIServer视频监控汇聚抖动检测算法优势

LntonAIServer视频质量诊断功能中的抖动检测是一个专门针对视频稳定性进行分析的功能。抖动通常是指视频帧之间的不必要运动,这种运动可能是由于摄像机的移动、传输中的错误或编解码问题导致的。抖动检测对于确保视频内容的平滑性和观看体验至关重要。 优势 1. 提高图像质量 - 清晰度提升:减少抖动,提高图像的清晰度和细节表现力,使得监控画面更加真实可信。 - 细节增强:在低光条件下,抖

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

poj 3974 and hdu 3068 最长回文串的O(n)解法(Manacher算法)

求一段字符串中的最长回文串。 因为数据量比较大,用原来的O(n^2)会爆。 小白上的O(n^2)解法代码:TLE啦~ #include<stdio.h>#include<string.h>const int Maxn = 1000000;char s[Maxn];int main(){char e[] = {"END"};while(scanf("%s", s) != EO

秋招最新大模型算法面试,熬夜都要肝完它

💥大家在面试大模型LLM这个板块的时候,不知道面试完会不会复盘、总结,做笔记的习惯,这份大模型算法岗面试八股笔记也帮助不少人拿到过offer ✨对于面试大模型算法工程师会有一定的帮助,都附有完整答案,熬夜也要看完,祝大家一臂之力 这份《大模型算法工程师面试题》已经上传CSDN,还有完整版的大模型 AI 学习资料,朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费

dp算法练习题【8】

不同二叉搜索树 96. 不同的二叉搜索树 给你一个整数 n ,求恰由 n 个节点组成且节点值从 1 到 n 互不相同的 二叉搜索树 有多少种?返回满足题意的二叉搜索树的种数。 示例 1: 输入:n = 3输出:5 示例 2: 输入:n = 1输出:1 class Solution {public int numTrees(int n) {int[] dp = new int

Codeforces Round #240 (Div. 2) E分治算法探究1

Codeforces Round #240 (Div. 2) E  http://codeforces.com/contest/415/problem/E 2^n个数,每次操作将其分成2^q份,对于每一份内部的数进行翻转(逆序),每次操作完后输出操作后新序列的逆序对数。 图一:  划分子问题。 图二: 分而治之,=>  合并 。 图三: 回溯:

最大公因数:欧几里得算法

简述         求两个数字 m和n 的最大公因数,假设r是m%n的余数,只要n不等于0,就一直执行 m=n,n=r 举例 以18和12为例 m n r18 % 12 = 612 % 6 = 06 0所以最大公因数为:6 代码实现 #include<iostream>using namespace std;/