题意
给定平面内的n个点,选出一个点集S,使得S里的所有点两两之间欧几里得距离不超过d,问|S|的最大值以及S里的点都有哪些。若答案有多种,输出任意一个。
第一行两个整数n和d,分别表示烤鸭店数和老板给魔法炮的路费。
n<=100,d<=10000
分析
题意就是要找一个最大团。
正解貌似是二分图最大独立集。
居然还有随机化这种玄学的算法。。。看来是我孤陋寡闻了。
只要每次随机一个序列,然后按顺序贪心地加点就好了。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<bitset>
using namespace std;
const int N=105;
int n,d,w[N];
bitset<N> ans,now;
bool f[N][N],vis[N];
struct data{int x,y;}a[N];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int get_dis(data a,data b)
{
return (a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y);
}
void Rand()
{
for (int i=1;i<=n;i++) swap(w[i],w[rand()%n+1]);
}
void solve()
{
memset(vis,0,sizeof(vis));
now.reset();
for (int i=1;i<=n;i++)
if (!vis[i])
{
now.set(w[i]);
for (int j=i+1;j<=n;j++)
if (!f[w[i]][w[j]]) vis[j]=1;
}
if (now.count()>ans.count()) ans=now;
}
int main()
{
n=read();d=read();
for (int i=1;i<=n;i++) a[i].x=read(),a[i].y=read(),w[i]=i;
for (int i=1;i<n;i++)
for (int j=i+1;j<=n;j++)
if (get_dis(a[i],a[j])<=d*d) f[i][j]=f[j][i]=1;
for (int i=1;i<=1000;i++) Rand(),solve();
printf("%d\n",ans.count());
for (int i=1;i<=n;i++) if (ans[i]) printf("%d ",i);
return 0;
}