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