Bootstrap

L2-013 红色警报 并查集

题目连接

题解:连通块问题显然要与并查集有关,而且C4比赛极喜欢出与并查集有关的知识。

这道题可以这样做,即我每次去掉一个城市的时候,都对剩余的城市重新建立并查集,然后判断联通块的数量有没有删减,如果联通块的数量没变说明没有破坏连通性,而如果改变了说明破坏了连通性,这样虽然很暴力,时间复杂度很高,但是仍然能过。。。不得不说数据很弱。

当然这道题也可以这样做,由于当攻陷一个城市的时候,我们需要把并查集分开,但这显然是不现实的,我们可以逆向思维,即倒着想。先将攻打到最后剩余的城市用并查集联通起来,然后倒着考虑每一个被攻打的城市,把这个城市加入到城市网络里,如果并查集合并次数大于1,那么说明这个城市的删除会导致连通性的破坏,那么发出警报。如果合并次数等于1,说明该城市被攻陷不会破坏连通性,因此不需要发出警报。另外需要注意,输出信息需要保存在栈中,在判断结束后,逆序输出生成的信息。

另外,我的代码第一次提交的时候只得了21分,原因是把两个城市加入并查集的时候忘记 判断之间有没有道路了。。。这么严重的问题都能得21分,可见数据只弱。。。

代码:

#include <cstdio>
#include <string>
#include <iostream>
#include <stack>
using namespace std;
const int MAX = 505;
int G[MAX][MAX];
int N,M;
int parent[MAX];
int rem[MAX];
stack<int> stk;
stack<string> outstk;
void init()
{
	for(int i = 0;i < MAX;i++) parent[i] = i; 
} 
int find(int x)
{
	if(parent[x] == x) return x;
	return parent[x] = find(parent[x]);
}
bool join(int x1,int x2)
{
	int p1 = find(x1);
	int p2 = find(x2);
	if(p1 != p2) {parent[p1] = p2; return true;}
	return false;
}
int main()
{
	init();
	scanf("%d%d",&N,&M);
	for(int i = 0;i < M;i++)
	{
		int u,v;
		scanf("%d%d",&u,&v);
		G[u][v] = G[v][u] = 1;
	}
	int K;scanf("%d",&K);
	if(K == N) outstk.push("Game Over.\n");
	while(K--)
	{
		int c;scanf("%d",&c);
		rem[c] = 1;
		stk.push(c);
	}
	for(int i = 0;i < N;i++) if(!rem[i]) for(int j = 0;j < N;j++) if(!rem[j] && G[i][j]) join(i,j);
	while(!stk.empty())
	{
		int u = stk.top();stk.pop();
		rem[u] = 0;
		int cnt = 0;
		for(int i = 0;i < N;i++)
			if(!rem[i] && G[u][i])
				if(join(u,i))
					cnt++; 
		if(cnt >= 2)
		{
			char tmp[100];
			sprintf(tmp,"Red Alert: City %d is lost!\n",u);
			outstk.push(tmp);
		}
		else
		{
			char tmp[100];
			sprintf(tmp,"City %d is lost.\n",u);
			outstk.push(tmp);
		}
	}
	while(!outstk.empty())
	{
		cout<<outstk.top();
		outstk.pop();
	}
}


;