题目就不赘述了。。。分别是HDU1213 HDU1856 HDU1272 HDU1301 HDU1102。。。
1.HDU1213,裸并查集,给你N个人,K组关系,然后认识的可以公用一个桌子,问最后N个人需要用多少张桌子。。。
思路:一开始N个人初始化为互不认识,则桌子数共为N,然后慢慢把相关的人用并查集找出相连,然后如果PRE[I]!=I的时候 每次减一就可以得到结果- -!
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int pre[1005];
int find(int x)
{
if(x==pre[x])
return pre[x];
pre[x]=find(pre[x]);
return pre[x];
}
void join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
pre[x]=y;
}
int main()
{
int t;
cin>>t;
int n,m;
while(t--)
{
cin>>n>>m;
int count=n;
int i;
int a,b;
for(i=0;i<1005;i++)
{
pre[i]=i;
}
for(i=0;i<m;i++)
{
cin>>a>>b;
join(a,b);
}
for(i=1;i<=n;i++)
{
if(pre[i]!=i)
count--;
}
cout<<count<<endl;
}
return 0;
}
HDU1856:给你N组关系,然后判断能够生成的一个最大集合的元素有多少个。。。记录一下N组关系里面最大的值是哪个,用num[i]记录集合中的元素个数,最后输出最大的就好了。。。唯一的坑就在于- - 当N=0时 是空集。。。要输出1。。。当然 cin cout输出流会TLE- - 。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=10000005;
int pre[maxn],num[maxn];
void init()
{
int i;
for(i=1;i<=maxn;i++)
{
pre[i]=i;
num[i]=1;
}
}
int find(int x)
{
if(x==pre[x])
return pre[x];
pre[x]=find(pre[x]);
return pre[x];
}
void join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
{
pre[x]=y;
num[y]+=num[x];
}
}
int main()
{
int n,a,b,i,sum;
int t;
while(scanf("%d",&n)!=EOF)
{ if(n==0)
{
printf("1\n");
continue;
}
t=0;
init();
for(i=0;i<n;i++)
{
scanf("%d%d",&a,&b);
if(a>t||b>t)
t=max(a,b);
join(a,b);
}
sum=0;
for(i=1;i<=t;i++)
{
if(num[i]>=sum)
sum=num[i];
}
cout<<sum<<endl;
}
return 0;
}
HDU1272 依然是并查集,用并查集判断是否是回路以及是否是单纯的树还是森林,如果X Y有共同的祖先,那么再把X Y关系加入进来的时候说明成环了 ,而每次将二者关系输入要注意标记他们被征用了而且如果一旦有mark[i]!=0&&pre[i]==i的情况时要注意根节点数+1,如果根节点的个数大于1证明这不是一棵树而形成了一个森林,也是不符合条件的。。。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int pre[100005];
int flag;
int mark[100005];
int find(int x)
{
if(x==pre[x])
return pre[x];
pre[x]=find(pre[x]);
return pre[x];
}
int join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
pre[x]=y;
else
flag=0;
}
int main()
{
int i,a,b;
while(cin>>a>>b)
{
if(a==-1&&b==-1)
return 0;
if(a==0&&b==0)
{
cout<<"Yes"<<endl;
continue;
}
for(i=1;i<100005;i++)
{
pre[i]=i;
mark[i]=0;
}
mark[a]=mark[b]=1;
flag=1;
join(a,b);
while(cin>>a>>b)
{
if(a==0&&b==0)
break;
join(a,b);
mark[a]=mark[b]=1;
}
int count=0;
for(i=1;i<100005;i++)
{
if(mark[i]!=0&&pre[i]==i)
count++;
if(count>1)
flag=0;
}
if(flag==1)
cout<<"Yes"<<endl;
else
cout<<"No"<<endl;
}
return 0;
}
HDU1301 裸的最小生成树,可以用PRIM做 但是KRUSKAL做复杂度更低- -而且可以增强对并查集的熟练程度。。。然后唯一要改善的就是原来的都是数字与数字之间的联系,这道题变成了字母与字母,等下转化一下就好- -而且要注意GETCHAR()。。。在每次加入关系的时候将边总数更新~
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int pre[100];
int n,ans;
struct node{
int s;
int e;
int w;
}edge[10000];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(x==pre[x])
return pre[x];
pre[x]=find(pre[x]);
return pre[x];
}
bool join(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y)
{
pre[x]=y;
return true;
}
return false;
}
int main()
{
while(cin>>n)
{
if(n==0)
break;
int i,j;
for(i=0;i<=30;i++)
{
pre[i]=i;
}
ans=0;
int sum=0;
for(i=1;i<n;i++)
{
char s1;
getchar();
cin>>s1;
int m;
cin>>m;
for(j=1;j<=m;j++)
{
char s2;
int cost;
getchar();
cin>>s2;
cin>>cost;
edge[ans].s=s1-'A';
edge[ans].e=s2-'A';
edge[ans].w=cost;
ans++;
}
}
sort(edge,edge+ans,cmp);
for(i=0;i<ans;i++)
if(join(edge[i].s,edge[i].e))
sum+=edge[i].w;
cout<<sum<<endl;
}
return 0;
}
HDU1102 类似于最小生成树,但不完全一样,他是给了你一些固定的边后,让你在剩下的边里面选取 求出最小总权值的问题,所以先用并查集把已经给定的关系加入,然后再按照权值大小,判断是否成环依次加入,得到最终所要得到的最小权。。。
#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
int pre[105];
int map[105][105];
struct node{
int x;
int y;
int w;
}edge[10000];
bool cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x)
{
if(x==pre[x])
return pre[x];
pre[x]=find(pre[x]);
return pre[x];
}
bool join(int x,int y)
{
x=find(x);
y=find(y);
if(x==y)
return false;
pre[x]=y;
return true;
}
int main()
{
int n;
int i,j,q,a,b,x,y;
int cost;
int count;
while(cin>>n)
{
for(i=1;i<=n;i++)
pre[i]=i;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
cin>>map[i][j];
count=0;
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
edge[count].x=i;
edge[count].y=j;
edge[count].w=map[i][j];
count++;
}
sort(edge,edge+count,cmp);
cin>>q;
for(i=0;i<q;i++)
{
cin>>a>>b;
a=find(a);
b=find(b);
if(a!=b)
pre[a]=b;
}
cost=0;
for(i=0;i<count;i++)
if(join(edge[i].x,edge[i].y)!=0)
cost+=edge[i].w;
cout<<cost<<endl;
}
return 0;
}
额 这五题都是基础题,厉害的人赶脚一个小时可能就能全部AC,但是我基础较差 花了蛮多时间,代码写挫了,勿怪o(╯□╰)o。