随机算法 - 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;
}

时间: 2024-10-13 11:55:12

随机算法 - HNU 13348 Finding Lines的相关文章

Finding Lines

Finding Lines 题目链接:https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=4967 概率 在听题解前毫无头绪,题解帮我打开了新世界的大门: 随机取一个点在答案直线(如果存在这个直线)上的概率是p%, 那么随机取到两个点构成的直线就是答案直线的概率是p%*p%: 也就是说,随机取到两个点构成的直线不是答案直线的概率为

随机算法 poj 2576 Tug of War

Tug of War Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 8187   Accepted: 2204 Description A tug of war is to be arranged at the local office picnic. For the tug of war, the picnickers must be divided into two teams. Each person must b

Miller_Rabin算法(随机算法,判断一个数是否是素数)

1 const int S = 20;//随机算法判定次数,S越大,判错概率越小 2 LL pow_mod(LL a, LL b, LL mod) { // a^b%mod 3 LL ans = 1; 4 a = a % mod; 5 while(b) { 6 if(b & 1) { 7 ans = (ans * a) % mod; 8 } 9 a = ( a * a ) % mod; 10 b >>= 1; 11 } 12 return ans; 13 } 14 bool check

POJ 3318:Matrix Multiplication(随机算法)

http://poj.org/problem?id=3318 题意:问A和B两个矩阵相乘能否等于C. 思路:题目明确说出(n^3)的算法不能过,但是通过各种常数优化还是能过的. 这里的随机算法指的是随机枚举矩阵C的一个位置,然后通过A*B计算是否能够得到矩阵C相应位置的数,如果不等,就直接退出了,如果跑过一定的数量后能够相等,那么就可以判断这个矩阵C等于A*B的.第一次见这样的题目...有点新奇. 暴力算法: 1 #include <cstdio> 2 using namespace std;

微信红包随机算法

最近看了一篇文章,讲微信红包随机算法的.感觉很不错,所以自己实现了下,并进行了简单测试. 算法 算法很简单,不是提前算好,而是抢红包时计算: 红包里的金额怎么算?为什么出现各个红包金额相差很大?答:随机,额度在0.01和剩余平均值*2之间. 实现 实现上述算法的逻辑主要是: public static double getRandomMoney(RedPackage _redPackage) { // remainSize 剩余的红包数量 // remainMoney 剩余的钱 if (_red

加权随机算法

加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项.这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10%.一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重.那么上述集合就成了: {A:5,B:2,C:2,D:1} 方法一: 扩展这个集合,使每一项出现的次数与其权重正相关.在上述例子这个集合扩展成:{A,A,A,A,A,B,B,C,C,D}然后就可以用均匀随机算法来从中选取. 好处:选取的

权重随机算法的java实现

一.概述 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的.如广告投放.负载均衡等. 如有4个元素A.B.C.D,权重分别为1.2.3.4,随机结果中A:B:C:D的比例要为1:2:3:4. 总体思路:累加每个元素的权重A(1)-B(3)-C(6)-D(10),则4个元素的的权重管辖区间分别为[0,1).[1,3).[3,6).[6,10).然后随机出一个[0,10)之间的随机数.落在哪个区间,则该区间之后的元素即为按权重命中的元素. 实现方法

由抽奖软件想到的随机算法总结

整整三年没更新博客了,今天和女友聊天,聊到了博客,就回来看看. 最近接触到抽奖软件,下载的源码是http://download.csdn.net/detail/ghz_sd/6918125,在这里为开源软件作出贡献的人致敬,这个软件的作者a米山,是个非常好的人,耐心的帮我调试,他的算法很简单,就是纯粹的random,用的rand()函数,我给他提了个需求,写一个作弊类,实现的功能是:指定人的中奖概率提高,配置文件类似于这样: <xml> <win> <name='a' pro

中科院随机算法课程(孙晓明主讲)topic list

如题,列出<随机算法>课程的topic list,以记录和供有兴趣的朋友研究. Lession1:生日悖论.生日攻击.两个常用数学工具(马尔科夫不等式.切比雪夫不等式) Lession2:radom quick sort(hw).矩阵乘法判定.min(max())=max(min()).复杂性类(BPP/RP/CORP/ZPP) Lession3:证明BPP2/3=BPP0.99.ZPP<=RP^CORP.Game Tree Lession4:Balls and Bins Lession