【DP】ants

题目简述: 

平面上有N只蚂蚁,每个蚂蚁的位置我们用一个整数坐标(x,y)表示。现在我们想知道这个平面上可能有几群蚂蚁。我们称一些蚂蚁组成一个群,当且仅当:这个群里任意两只蚂蚁之间的距离,都严格小于这个群里任意一只蚂蚁到不属于这个群的任意一只蚂蚁之间的距离。两只蚂蚁之间的距离为sqrt((x1-x2)^2+(y1-y2)^2)。对重合蚂蚁的特别声明:一只蚂蚁始终可以作为一个群,即可以认为自己到自己距离为-oo,若有4只重合蚂蚁,则答案为1001。


我们思考: 如果按照边权加入边, 若某一时刻若有一个连通块是一个完全图, 那么这个块是可以组成一个群的。

一个思路: 记录当前群数 Gr, 我们不断加边进去(相同的边必须一起加进去), 不断减小Gr, 当某一个时刻这个图是稳定的(每个连通块单独成群) 那么ans[Gr]= 1。

显然这是不行的。

但我们考虑, 如果一个群 G 是由 G1 + G2 组成的, 群F 是由 F1 + F2 组成的(F1 , F2, G1, G2都是可以单独成群) , 那么 群数有可能为 2, 3, 4.

若G1 - G2,   F1  - F2同时合并, 如果按照上面的思路, Gr将直接-2, 所以 3 这种情况将直接忽略。


所以对于一个连通块, 我们记录一个数组, 表示这个群可以由几个群构成的。

每次合并, 只要合并背包就行了。


Pro:

#include<cstdio>
#include<cstring>
#include<set>
#include<string>
#include<algorithm>
#include<ctime>
#define sett set<int>::iterator

using namespace std;

struct pa{int x,y,l;} p[5010],dis[1000010];

bool ans[5010];

int n,fa[5010],tot,size[5010],maxl[5010],nn[1000010],num;

set<int> can[5010],node[5010];

bool cmp(pa a,pa b) {return a.l<b.l;}

int sqr(int x) {return x*x;}

int getfa(int x){if (fa[x]!=x) fa[x]=getfa(fa[x]); return fa[x];}

void update(int &a,int b){if (b>a) a=b;}

int Dis(int i,int j){return sqr(p[i].x-p[j].x)+sqr(p[i].y-p[j].y);}

int main()
{
	freopen("ant.in","r",stdin); freopen("ant.out","w",stdout);
	for (scanf("%d",&n);n;scanf("%d",&n))
	{
		for (int i=0;i<n;i++) 
        {
            scanf("%d%d",&p[i].x,&p[i].y);
			can[i].clear(),can[i].insert(0);
			fa[i]=i,size[i]=1; maxl[i]=0;
			node[i].clear(); node[i].insert(i);
		}
		tot=0;
		for (int i=0;i<n;i++)
			for (int j=0;j<n;j++) if (i!=j) dis[tot].x=i,dis[tot].y=j,dis[tot++].l=Dis(i,j);
		sort(dis,dis+tot,cmp);
		for (int i=0;i<tot;)
		{
			int j=i;
			num=0;
			for (;j<tot && dis[j].l==dis[i].l;j++)
			{
				int f1=getfa(dis[j].x),f2=getfa(dis[j].y);
				if (f1!=f2)
				{
					set<int> now; now.clear();
					for (sett ii=can[f1].begin();ii!=can[f1].end();ii++)
						for (sett jj=can[f2].begin();jj!=can[f2].end();jj++)
							now.insert((*ii)+(*jj));
					for (sett ii=now.begin();ii!=now.end();ii++) can[f1].insert(*ii);
					now.clear();
					update(maxl[f1],maxl[f2]);
					for (sett ii=node[f1].begin();ii!=node[f1].end();ii++)
						for (sett jj=node[f2].begin();jj!=node[f2].end();jj++)
						update(maxl[f1],Dis(*ii,*jj));
					for (sett jj=node[f2].begin();jj!=node[f2].end();jj++) node[f1].insert(*jj);
					fa[f2]=f1; size[f1]+=size[f2];
				}
				nn[num++]=f1;				
			}
			for (int k=0;k<num;k++)
				if (fa[nn[k]]==nn[k] && maxl[nn[k]]<=dis[i].l) can[nn[k]].insert(size[nn[k]]-1);
			i=j;
		}
		memset(ans,0,sizeof ans);
		for (int i=0;i<n;i++) if (size[i]==n) for (sett j=can[i].begin();j!=can[i].end();j++) ans[n-(*j)]=true;
		for (int i=1;i<=n;i++) if (ans[i]) printf("1");else printf("0");
		puts("");
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值