The Suspects(POJ 1611)
题意:
SARS 是一种传染性疾病,在某大学爆发了这种既疾病。为了减少对他人的传播,最好的方式是将病人与其他人隔离开。
现在一名学生可能加入多个团体。一个团体的一名学生疑似患病,其他学生也要被隔离。编写一个找到所有患病嫌疑人的程序。
输入:输入文件包含几种情况。每个测试用例以一行中的两个整数n和m开始,其中n是学生数,m是团体的数量。0
题解:要查询在同一个集合的`成员的数量,并查集模板题。
#include
#include
#include
#include
using namespace std;
const int N=3e4+7;
int pre[N];
int find_set(int x)
{
if (x!=pre[x])
return pre[x]=find_set(pre[x]);
return x;
}
void merge_set(int x,int y)
{
int fx=find_set(x);
int fy=find_set(y);
if (fx!=fy)
pre[fx]=fy;
}
int main()
{
int n,m,num;
while (~scanf("%d %d",&n,&m)&&(n||m))
{
for (int i=0; iwhile (m--)
{
scanf("%d",&num);
int a,b;
scanf("%d",&a);
//找哪些学生在同一个集合内
for (int i=1; iscanf("%d",&b);
merge_set(a,b);
}
}
//找和0在同一个集合内的学生并计数
int ans=0;
for (int i=0; iif (find_set(i)==pre[0])
ans++;
printf("%d\n",ans);
}
return 0;
}
Agri-Net(POJ 1258)
题意:
John是市长,他当选市长的承诺是为城镇提供互联网连接。John为他的农场订购了网线,并将与其他农民分享他的连接。为了最大限度地降低成本,他希望将最少量的光纤用于将农场连接到所有其他农场。
给出连接每对农场需要多少光纤的列表,您必须找到将它们连接在一起所需的最少光纤数量。每个农场必须与其他农场连接。任何两个农场之间的距离不会超过1e5。
输入:包括几种情况。对于每种情况,第一行包含农场数N(3 <= N <= 100)。以下行包含N x N的矩阵,表示两个农行之间的距离。数字之间用空格隔开。
输出:对于每种情况,输出一个整数长度,它是连接整个服务器集所需的最小光纤长度之和。
题解: 根据题意要找图的最小生成树,prim和kruskal都可以写。
//prim算法实现
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e2+7;
int mapp[N][N];
int prim(int s,int n)
{
int dis[N],v[N],now,m,sum=0;
for (int i=0; imemset(v,0,sizeof(v));
v[s]=1,dis[s]=0;
for (int i=1; i//遍历n-1 次
{
m=now=inf;
for (int j=0; jif (!v[j]&&m>dis[j])
{
m=dis[j];
now=j;
}
}
//提前退出,不是连通图
if (now==inf)
return -1;
v[now]=1,sum+=m;
//更新已加入生成树点与未加入生成树点之间的最小权值
for (int j=0; jif (!v[j]&&dis[j]>mapp[now][j])
dis[j]=mapp[now][j];
}
return sum;
}
int main()
{
int n;
while (~scanf("%d",&n))
{
for (int i=0; ifor (int j=0; jscanf("%d",&mapp[i][j]);
int sum=prim(0,n);
if (sum==-1)
printf("-1\n");
else
printf("%d\n",sum);
}
return 0;
}
//kruskal算法实现
#include
#include
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int N=1e4+7;
int pre[N];
int sum;
struct town
{
int x,y,power;
} edge[N];
int cmp(town a,town b)
{
return a.powerint find_set (int x)
{
int r=x;
while (pre[r]!=r)
r=pre[r];
int i=x;
int j;
while (i!=r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void merge_set(town a)
{
int x=find_set(a.x);
int y=find_set(a.y);
if (x!=y)
{
pre[x]=y;
sum+=a.power;
}
}
int main()
{
int n;
while (~scanf("%d",&n))
{
for (int i=0; i<=n; i++)
pre[i]=i;
sum=0;
int cnt=0;//记录边的条数
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
{
edge[cnt].x=i;
edge[cnt].y=j;
scanf("%d",&edge[cnt].power);
if (edge[cnt].power==0)
edge[cnt].power=MAX;
cnt++;
}
sort (edge+1,edge+cnt+1,cmp);
for (int i=1; i<=cnt; i++)
{
merge_set(edge[i]);
}
printf("%d\n",sum);
}
return 0;
}
Bad Cowtractors(POJ 2377)
题意:
Bessie受雇在Farmer John的N(2 <= N <= 1,000)个谷仓中建立廉价的互联网网络。这些谷仓的编号为1..N。 FJ已经进行了一些测量,发现M(1 <= M <= 20,000)条可能在成对的谷仓之间建立路线。每个可能的连接路由具有相关的成本C(1 <= C <= 100,000)。农夫约翰想花最少的钱来连接网络,他甚至不愿意付贝茜。
意识到农夫约翰不会付钱给她,贝茜决定做最糟糕的工作。她必须决定,以便(1)这些连接的总成本尽可能大,(2)所有的谷仓连接在一起(这样就使任意谷仓可以到达其他谷仓的),以及(3)使连接之间没有循环(Farmer John很容易检测到)。
题解:最大生成树,把prim和kruskal的代码稍微一改就可以了。这道题只用kruskal来着,用prim算法实现的代码自己写吧。记录n个谷仓之间需要建n-1条路才能使任意两个谷仓之间都可达,记录建了几条路,可以确定n个谷仓之间是否有最大生成树。
//kruskal算法实现
#include
#include
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int sum,coun;
struct town
{
int x,y,power;
} edge[MAXE];
int cmp(town a,town b)
{
return a.power>b.power;
}
int find_set (int x)
{
int r=x;
while (pre[r]!=r)
r=pre[r];
int i=x;
int j;
while (i!=r)
{
j=pre[i];
pre[i]=r;
i=j;
}
return r;
}
void merge_set(town a)
{
int x=find_set(a.x);
int y=find_set(a.y);
if (x!=y)
{
pre[x]=y;
sum+=a.power;
coun++;//记录建路条数
}
}
int main()
{
int n,m;
while (~scanf("%d %d",&n,&m))
{
for (int i=0; i<=n; i++)
pre[i]=i;
for (int i=0; iscanf("%d %d %d",&edge[i].x,&edge[i].y,&edge[i].power);
}
sort (edge,edge+m,cmp);
sum=coun=0;
for (int i=0; iif (coun!=n-1)
printf("-1\n");
else
printf("%d\n",sum);
}
return 0;
}
畅通工程1(HDU 1232)
题意:
中文,题意不解释。
题解: 并查集经典题。求给出的图有几个集合组成。两个集合需要一条路联通成一个集合,三个集合需要两条路连接成一个集合,n个集合需要n-1条路连接成一个集合。当pre[ i ]== i;时为一个集合。
#include
#include
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int find_set (int x)
{
if (pre[x]!=x)
return pre[x]=find_set(pre[x]);
return x;
}
void merge_set(int x,int y)
{
int fx=find_set(x);
int fy=find_set(y);
if (fx!=fy)
{
pre[fx]=fy;
}
}
int main()
{
int n,m;
while (~scanf("%d",&n)&&n)
{
scanf("%d",&m);
for (int i=0; i<=n; i++)
pre[i]=i;
int u,v;
for (int i=0; iscanf("%d %d",&u,&v);
merge_set(u,v);
}
int coun=0;
for (int i=1; i<=n; i++)
{
if (pre[i]==i)
coun++;
}
printf("%d\n",coun-1);
}
return 0;
}
畅通工程2(HDU 1863)
题意:
中文题,不解释。
题解: 裸的求最小生成树。两种算法都可以用,随便挑一种用就可以。
//prim算法实现
#include
#include
#include
#include
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1e2+7;
int mapp[N][N];
int prim(int s,int n)
{
int dis[N],v[N],now,m,sum=0;
for (int i=1; i<=n; i++)
dis[i]=mapp[s][i];
memset(v,0,sizeof(v));
v[s]=1,dis[s]=0;
for (int i=1; i//遍历n-1 次
{
m=now=inf;
for (int j=1; j<=n; j++)
{
if (!v[j]&&m>dis[j])
{
m=dis[j];
now=j;
}
}
//提前退出,不是连通图
if (now==inf)
return -1;
v[now]=1,sum+=m;
//更新已加入生成树点与未加入生成树点之间的最小权值
for (int j=1; j<=n; j++)
if (!v[j]&&dis[j]>mapp[now][j])
dis[j]=mapp[now][j];
}
return sum;
}
int main()
{
int n,m;
while (~scanf("%d %d",&n,&m)&&n)
{
int u,v,w;
memset(mapp,inf,sizeof(mapp));
for (int i=0; iscanf("%d %d %d",&u,&v,&w);
if (mapp[u][v]>w)
mapp[u][v]=mapp[v][u]=w;
}
int sum=prim(1,m);
if (sum==-1)
printf("?\n");
else
printf("%d\n",sum);
}
return 0;
}
How Many Tables(HDU 1213)
题意: 今天是Ignatius的生日。他邀请了很多朋友。现在是晚餐时间。伊格纳修斯想知道他至少需要多少桌子。你必须注意到并非所有的朋友都互相认识,而且所有的朋友都不想和陌生人呆在一起。
题解: 并查集,查找有多少个集合。
#include
#include
#include
#include
using namespace std;
const int MAX=0x3f3f3f3f;
const int MAXV=1e3+7;
const int MAXE=2e4+7;
int pre[MAXV];
int find_set (int x)
{
if (pre[x]!=x)
return pre[x]=find_set(pre[x]);
return x;
}
void merge_set(int x,int y)
{
int fx=find_set(x);
int fy=find_set(y);
if (fx!=fy)
{
pre[fx]=fy;
}
}
int main()
{
int t,n,m;
scanf("%d",&t);
while (t--)
{
scanf("%d %d",&n,&m);
for (int i=0; i<=n; i++)
pre[i]=i;
int u,v;
for (int i=0; iscanf("%d %d",&u,&v);
merge_set(u,v);
}
int coun=0;
for (int i=1; i<=n; i++)
{
if (pre[i]==i)
coun++;
}
printf("%d\n",coun);
}
return 0;
}