【概率與隨機化算法】 最小圓覆蓋&& noi2007 神奇口袋


         

            鍾誠的wc論文選做了兩道題,一直忘記發上來了。 

            點的最小圓覆蓋算法:

                    把點隨機打亂之後, 先取兩個點,初始化圓,然後繼續加點, 如果在當前圓的外面,那麼————》這個點一定在“更新圓”上,那麼問題轉化爲確定一個點,求一個圓覆蓋,遞歸後繼續做,同樣問題可以轉化爲確定兩個點,求一個圓覆蓋。

                    看似複雜度超高的算法,由於點是隨機的,複雜度變成了驚人的線性! 

       bzoj1337

# include <cstdlib>
# include <cstdio>
# include <cmath>

using namespace std;

struct point { double x,y,z;};
struct bans  { double x,y,z,r;};

const int maxn = 200000;
const double eps=1e-6;
point sd[maxn];
bans ans;
int n, i;

void swap(point &x, point &y){point tmp = x; x=y; y=tmp;};
inline double sqr(double x) {return x*x;};
inline double dist(point u, point v) { return sqrt(sqr(u.x-v.x)+sqr(u.y-v.y));};
inline bool stayout(point u, bans v)
{
  point w; w.x=v.x;w.y=v.y;w.z=v.z;
  return dist(u,w)-v.r>eps?true:false;
}

bool bezero(double x) {return x <eps&& x>-eps? true:false;}
point cross(point u, point v)
{
  point ask;
  ask.x = u.y*v.z-u.z*v.y; 
  ask.y = u.z*v.x-u.x*v.z;
  ask.z = u.x*v.y-u.y*v.x;
  if (!bezero(ask.z)) 
      ask.x/=ask.z,ask.y/=ask.z,ask.z=1;
  return ask;
}
point makeline(point u, point v)
{
  point d, c;d.x=(u.x+v.x)/2;d.y=(u.y+v.y)/2;d.z=1;
  c.x=d.x-(u.y-v.y); c.y=d.y+(u.x-v.x); c.z=1;
  return cross(c,d);
}
bans makecircle(point u, point v, point w)
{
  point a = makeline(u,v); 
  point b = makeline(v,w);
  bans ask; point p = cross(a,b); ask.x=p.x,ask.y=p.y,ask.z=p.z;
  ask.r=dist(u,p);
  return ask;
}

bans updata_two(int tail, point ud, point vd)
{
  int i; bans ans; ans = (bans){(ud.x+vd.x)/2,(ud.y+vd.y)/2,1,dist(ud, vd)/2};
  for (i = 1; i <= tail; i++)
    if (stayout(sd[i], ans)) 
           ans = makecircle(sd[i], ud, vd);
  return ans;
}

bans updata_one(int tail, point td)
{
  bans ans;  ans = (bans){(td.x+sd[1].x)/2, (td.y+sd[1].y)/2, 1, dist(td, sd[1])/2};
  for (int i = 2; i <= tail; i++)
    if (stayout(sd[i], ans)) 
           ans = updata_two(i-1, sd[i], td);
  return ans;
}

int main()
{ 
  //freopen("1337.in", "r", stdin);
  //freopen("1337.out", "w", stdout);
  scanf("%d", &n);  srand(n);
  for (i = 1; i <= n; i++) scanf("%lf%lf", &sd[i].x, &sd[i].y);
  //for (i = 1; i <= n; i++) swap(sd[((rand()<<15)+rand())%n+1], sd[((rand()<<15)+rand())%n+1]);
  for (i = 1; i <= n; i++) sd[i].z = 1;
  ans.x = (sd[1].x+sd[2].x)/2, ans.y = (sd[1].y+sd[2].y)/2; ans.z = 1; ans.r = dist(sd[1], sd[2])/2;
  for (i = 3; i <= n; i++)
    if (stayout(sd[i],ans)) 
         ans = updata_one(i-1, sd[i]);
  printf("%.3lf", ans.r);
  return 0;
}

 

    noi2007 bag

    題目忽略。

    其實是一道相當簡單的題目,唯一的難點是想到球的選取順序是沒有意義的,接下來的部分就是分解質因數+高精。

     可以看看這裏的較爲詳細的解釋:http://blog.csdn.net/huyuncong/article/details/7262254

 

# include <cstdlib>
# include <cstdio>
# include <cmath>
# include <cstring>

using namespace std;

const int maxn = 20000;
bool flag[maxn+5];
int pr[maxn+5];
int ans1[10000], ans2[10000];
int M,N,t,n,d,tot;
int deno[maxn], nume[maxn], g, a[maxn], use[maxn];
bool work[maxn];

inline int max(int x, int y){return x> y?x:y;};
void prepare()
{
	int i, j;
	memset(flag, true, sizeof(flag));
	for (i = 2; i <= 20000; i++)
	if (flag[i])
	{
		pr[++pr[0]] = i;
		for (j = i+i; j <= 20000; j+= i)
		  flag[j] = false;
	}
}

void mul(int *ans, int d)
{
	int i;
	for (i = 1; i <= ans[0]; i++) ans[i]*= d;
	for (i = 1; i <= ans[0]-1; i++) 
	 if (ans[i] >= 1000) ans[i+1]+= ans[i]/1000, ans[i]%= 1000;
	for (;ans[ans[0]] >= 1000;ans[ans[0]+1]=ans[ans[0]]/1000, ans[ans[0]]%=1000, ans[0]++);
}

int main()
{
	int i,j,unt = 0, c;
	//freopen("bag.in", "r", stdin);
	//freopen("bag.out", "w", stdout);
	scanf("%d%d%d", &t,&n,&d);
	for (i = 1; i <= t; i++) {scanf("%d", &a[i]); tot+= a[i];};
	for (i = 1; i <= n; i++) 
	{
		 scanf("%d%d", &c, &g);
		 use[g]++; 
	};
	for (i = 1; i <= n; i++) 
	  ++N,deno[N]=tot+(N-1)*d;
	for (i = 1; i <= t; i++)
	  for (j = 1; j <= use[i]; j++)
	    nume[++M] = a[i]+(j-1)*d;
	prepare(); ans1[0]=ans2[0]=1; ans1[1]=ans2[1]=1;
	for (i = 1; i <= pr[0]; i++)
	{
		int high = 0; 
		for (j = 1; j <= M; j++)
		  for (;nume[j] % pr[i] == 0;high++, nume[j]/=pr[i]);
		for (j = 1; j <= N; j++)
		  for (;deno[j] % pr[i] == 0;high--, deno[j]/=pr[i]);
		if (high > 0)
		 for (j = 1; j <= high; j++) mul(ans1, pr[i]);
		else for (j = 1, high=-high; j <= high; j++) mul(ans2, pr[i]);
	}
	for (printf("%d", ans1[ans1[0]]), i = ans1[0]-1; i >0; i--) 
	   printf("%03d", ans1[i]);
	printf("/");
	for (printf("%d", ans2[ans2[0]]), i = ans2[0]-1; i >0; i--)
	   printf("%03d", ans2[i]);
	return 0;
}

    當然,論文中提到了以期望o(n)的複雜度判斷半平面交是否有解的方法,ldl寫了之後聲稱想吐,看來性價比不高,不如直接寫半平面交。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章