题目:
合纵连横
时间限制:
1000 ms | 内存限制:
65535 KB
难度:
3
描述
乱世天下,诸侯割据。每个诸侯王都有一片自己的领土。但是不是所有的诸侯王都是安分守己的,实力强大的诸侯国会设法吞并那些实力弱的,让自己的领土面积不断扩大。而实力弱的诸侯王为了不让自己的领土被吞并,他会联合一些其他同样弱小的诸侯国,组成联盟(联盟不止一个),来共同抵抗那些强大的诸侯国。 强大的诸侯国为了瓦解这些联盟,派出了最优秀的间谍来离间他们,使一些诸侯国退出联盟。最开始,每个诸侯国是一个联盟。
有两种操作
1、U x y 表示x和y在同一个联盟。(0≤x,y<n)
2、D x 表示x退出联盟。
输入
多组测试数据
第一行两个数,n和m(1 ≤ n≤ 10^5, 1 ≤ m ≤10^5),分别表示诸侯国的个数和操作次数。
接下来有m行操作
输出
Case #1: 2
Case #2: 9
输出联盟的个数
样例输入
5 7
U 0 1
U 1 2
U 0 3
D 0
U 1 4
D 2
U 0 2
10 1
U 0 9
样例输出
Case #1: 2
Case #2: 9
心得:
:一开始我用数组元素增减来做,没有完成,然后看到大神之作,想法清晰易懂
考察 交并集删减算法
将诸侯国当成食物,用food[]储存所装入的箱子点编号。food[2]=2;表示编号为2的食物放入编号为2的箱子,box[2]=3表示编号为2的箱子的钩子钩上编号为3的箱子。
如果有诸侯国联盟,首先查看当前是否属于一个联盟,find()函数寻找箱子连接的根源。若根源相同,表示属于同一棵树(同一个集合);若不相同,将两者连接。
如果有诸侯国退出联盟,将箱子里的食物拿出来,放在另一个空箱子里,并将这个箱子的钩子勾住自己。在这个过程中,箱子依然在,不会影响节点。
超时问题:
find()函数采用递归方法完成根源的寻找,一开始我是写return find(box[x]);结果 超时了,然后改成return box[x]=find(box[x]);解决了超时问题,原因在于前者个箱子之间以 链状(或数状) 相连,箱子的根源为树根,寻找根源可能要好多步;后者是为 散发式,中间一点为根源,寻找根源的时候只需要一步,减少了循环次数
代码<C语言>:
#include <stdio.h>
#include <string.h>
int box[1000050];
int food[1000050];
int mark[1000050];//用来标志根源箱子
int find(int x)//寻找箱子所连接时最初的根源(根源的箱子上面的钩子必会钩在自己身上)
{
if(x==box[x])//如果箱子就连接在自己身上
return x;
return box[x]=find(box[x]);//递归运算,最终总会满足if条件
}
void Com(int x,int y)
{
int fx,fy;
fx=find(x);
fy=find(y);
if(fx!=fy)//如果两个箱子的根源不一样
box[fx]=fy;//将第一个箱子的根源箱子上的锁连接到第二个箱子的根源箱子
}
int main()
{
char ch;
int n,m,x,y,ans,t,i,count=1;
while(scanf("%d%d",&n,&m)!=EOF)
{
t=n;//为下面要用到空箱子做准备
for(i=0;i<n;i++)//初始状态
{food[i]=i;//将食物i放入编号为i的box里
box[i]=i;//将编号为i的box上的钩钩自己
}
for(i=0;i<m;i++)
{
getchar();//读取回车键
scanf("%c",&ch);
if(ch=='U')
{
scanf("%d%d",&x,&y);
Com(food[x],food[y]);//合并就是把箱子的挂钩钩上。
}
else //如果退出联盟
{
scanf("%d",&x);
food[x]=t;//拿掉box里的food,并把食物放入另一个空箱子,即编号为t的box,前面t=n就是为这个做准备
box[t]=t;//并把这个box的挂钩钩自己
t++;
}
}
ans=0;
memset(mark,0,sizeof(mark));//将数组中的元素全部赋值为0
for(i=0;i<n;i++)
{
if(mark[find(food[i])]==0)
{mark[find(food[i])]=1;//赋值为1后,再有箱子的有相同的根源,就不会等于0了,就不会增加ans(很妙!)
ans++;}
}
printf("Case #%d: %d\n",count++,ans);//拥有同一个根源的箱子为一个联盟
}
return 0;
}
AC情况: