钟诚的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写了之后声称想吐,看来性价比不高,不如直接写半平面交。