【二分图】【并查集】XVII Open Cup named after E.V. Pankratiev Stage 14, Grand Prix of Tatarstan, Sunday, April 2, 2017 Problem L. Canonical duel
给你一个网格(n<=2000,m<=2000),有一些炸弹,你可以选择一个空的位置,再放一个炸弹并将其引爆,一个炸弹爆炸后,其所在行和列的所有炸弹都会爆炸,连锁反应。
问你所能引爆的最多炸弹数。
转化成:
将行列当成点,炸弹当成边,然后你可以给这个二分图加1条边,问你最大的连通块的边的数量。
可以通过枚举所有可以建的边,通过并查集来尝试更新答案。由于一条边必然会让总度数+2,所以一个连通块的边数是所有点的度数之和/2。
并查集不必要动态维护集合的大小,一开始就建好并查集,提前统计好即可。
最后枚举的时候不会再在并查集之间再连边了,只会直接去更新答案。
队友的代码:
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; int Ans,num[6000],a[2003][2003],fa[6000],ans[6000],n,m,ansx,ansy; char ch; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void un(int x,int y) { int X=get(x),Y=get(y); if(X!=Y) fa[X]=Y; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { while(ch=getchar(),ch!='+'&&ch!='.'); a[i][j]=ch; if(ch=='+') { num[i]++; num[j+n]++; } } } Ans=0; for(int i=1;i<=n+m;++i) { fa[i]=i; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(a[i][j]=='+') { un(i,j+n); } } for(int i=1;i<=n+m;++i) { ans[get(i)]+=num[i]; } for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) { if(a[i][j]=='.') { int nowans=ans[get(i)]; if(get(i)!=get(j+n)) { nowans+=ans[get(j+n)]; } if(nowans/2>Ans) { Ans=nowans/2; ansx=i; ansy=j; } } } printf("%d\n",Ans); if(Ans) { printf("%d %d\n",ansx,ansy); } return 0; }
——The Solution By AutSky_JadeK From UESTC
转载请注明出处:http://www.cnblogs.com/autsky-jadek/