、最小生成树:v + v + value布线问题
布线问题:由于安全问题,只能选择一个楼连接到外界供电设备。
/*
1
v e
4 6(4个顶点,正好六个边)
1 2 10
2 3 10
3 1 10
1 4 1
2 4 1
3 4 1
1 3 5 6(连接到外界供电设备所需要的费用)
*/
#include<iostream>
using namespace std;
#define MAX 505
#define MAXCOST 0x7fffffff
int graph[MAX][MAX];
int prim(int graph[][MAX], int n)
{
int lowcost[MAX];
int mst[MAX];
int i, j, min, minid, sum = 0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = MAXCOST;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
//cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
}
return sum;
}
int main()
{
int i, j, k, m, n;
int x, y, cost;
int zushu;
cin>>zushu;
while(zushu--)
{
cin >> m >> n;//m=顶点的个数,n=边的个数
for (i = 1; i <= m; i++)
{
for (j = 1; j <= m; j++)
{
graph[i][j] = MAXCOST;
}
}
for (k = 1; k <= n; k++)
{
cin >> i >> j >> cost;
graph[i][j] = cost;
graph[j][i] = cost;
}
cost = prim(graph, m);
int min_v=MAXCOST;
for(int i=1;i<=m;++i)
{
int t;
cin>>t;
if(t<min_v)
min_v=t;
}
cout <<cost+min_v<< endl;
}
return 0;
}
3、prim算法之矩阵类型
/*
1(组数)
3(3*3的矩阵)
0 990 692
990 0 179
692 179 0
//一个有n个顶点的无向图最多几条边 n(n-1)/2
//具有n个顶点的无向图,至少应有多少n-1条边才能确保是一个连通图
*/
#include "stdio.h"
#include "string.h"
#define N 500
#define INT 10000
bool vis[N];
int dis[N];
int a[N][N];
int main(){
int t;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
int i,j,temp,k;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;++i){
for(j=1;j<=n;++j){
scanf("%d", &a[i][j]);
}
}
for(i=1;i<=n;++i){
dis[i]=INT;
}
dis[1]=0;
for(i=1;i<=n;++i){
temp=INT;
k=0;
for(j=1;j<=n;++j){
if(!vis[j]&&dis[j]<temp){
temp=dis[j];
k=j;
}
}
vis[k]=1;
for(j=1;j<=n;++j){
if(!vis[j]&&dis[j]>a[k][j]){
dis[j]=a[k][j];
}
}
}
int max=0;
for(i=1;i<=n;++i){
if(max<dis[i])
max=dis[i];
}
printf("%d\n",max);
}
return 0;
}
4、输出最小生成树个边权值累加和(矩阵类型)
4
0 4 9 21
4 0 8 17
9 8 0 16
21 17 16 0
#include <stdio.h>
#include <string.h>
#define MaxInt 0x3f3f3f3f
#define N 110
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
visited[1]=1;pos=1;
for(i=1;i<=n;i++)
if(i!=pos) low[i]=map[pos][i];
for(i=1;i<n;i++)
{
min=MaxInt;
for(j=1;j<=n;j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];pos=j;
}
result+=min;
visited[pos]=1;
//更新权值
for(j=1;j<=n;j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return result;
}
int main()
{
int i,v,j,ans;
while(scanf("%d",&n)!=EOF)
{
memset(map,MaxInt,sizeof(map));
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
scanf("%d",&v);
map[i][j]=map[i][j]=v;
}
ans=prim();
printf("%d\n",ans);
}
return 0;
}
5、求出最小生成树中边的最大值(矩阵类型)
1
3
0 990 692
990 0 179
692 179 0
692
#include<stdio.h>
#define MAX 505
#define inf 999999
int c[MAX][MAX];
int n;
void prim()
{
int lowcost[MAX ];
int closest[MAX ];
bool s[MAX ];
s[1]=true;
for(int i=2;i<=n;i++)
{
lowcost[i]=c[1][i];
closest[i]=1;
s[i]=false;
}
for(int i=1;i<=n;i++)
{
int min=inf;
int j=i;
for(int k=2;k<=n;k++)
if((lowcost[k]<min)&&(!s[k]))
{
min=lowcost[k];
j=k;
}
//cout<<j<<" "<<closet[j]<<endl;输出最小生成树的路径。
s[j]=true;
for(int k=2;k<=n;k++)
{
if((c[j][k]<lowcost[k])&&(!s[k]))
{
lowcost[k]=c[j][k];
closest[k]=j;
}
}
}
//最小生成树的边值已经放大lowcost数组中了。遍历一下就可以得到最大最小值。
int result=-1;
for(int i=2;i<=n;i++)
{
if(result<lowcost[i])
result=lowcost[i];
}
printf("%d\n",result);
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
scanf("%d",&c[i][j]);
}
}
prim();
}
return 0;
}
6、prim算法变形之引水工程(矩阵型)完全矩阵
/*1
5
0 5 4 4 3 6
5 0 2 2 2 2
4 2 0 3 3 3
4 2 3 0 4 5
3 2 3 4 0 1
5 2 3 5 1 0*/
#include <stdio.h>
#include <string.h>
#define MaxInt 0x3f3f3f3f
#define N 303
int map[N][N],low[N],visited[N];
int n;
int prim()
{
int i,j,pos,min,result=0;
memset(visited,0,sizeof(visited));
visited[1]=1;pos=1;
for(i=1;i<=n;i++)
if(i!=pos) low[i]=map[pos][i];
for(i=1;i<n;i++)
{
min=MaxInt;
for(j=1;j<=n;j++)
if(visited[j]==0&&min>low[j])
{
min=low[j];
pos=j;
}
result+=min;
visited[pos]=1;
for(j=1;j<=n;j++)
if(visited[j]==0&&low[j]>map[pos][j])
low[j]=map[pos][j];
}
return result;
}
int main()
{
int i,v,j,ans,zushu;
scanf("%d",&zushu);
int a[303];
while(zushu--)
{
scanf("%d",&n);
memset(map,MaxInt,sizeof(map));
map[1][1]=0;
for(i=2;i<=n+1;++i)
{
scanf("%d",&map[i][1]);
map[1][i]=map[i][1];
}
getchar();
for(i=2;i<=n+1;i++)
for(j=2;j<=n+1;j++)
{
scanf("%d",&v);
map[i][j]=map[i][j]=v;
}
n=n+1;
ans=prim();
printf("%d\n",ans);
}
return 0;
}
7、Kruskal(克鲁斯卡尔)(不是完全边,v+v+value)
/*
但不一定有直接的公路相连,只要能间接通过公路可达即可
村庄从1到M编号。当N为0时,全部输入结束,相应的结果不要输出。
对每个测试用例,在1行里输出全省畅通需要的最低成本。
若统计数据不足以保证畅通,则输出“?”。
Sample Input
3 3
1 2 1
1 3 2
2 3 4
1(e) 3(v)
2 3 2
0 100
Sample Output
3
?
*/
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 100
int father[MAX], son[MAX];
int v, l;
typedef struct Kruskal //存储边的信息
{
int a;
int b;
int value;
};
bool cmp(const Kruskal & a, const Kruskal & b)
{
return a.value < b.value;
}
int unionsearch(int x) //查找根结点+路径压缩
{
return x == father[x] ? x : unionsearch(father[x]);
}
bool join(int x, int y) //合并
{
int root1, root2;
root1 = unionsearch(x);
root2 = unionsearch(y);
if(root1 == root2) //为环
return false;
else if(son[root1] >= son[root2])
{
father[root2] = root1;
son[root1] += son[root2];
}
else
{
father[root1] = root2;
son[root2] += son[root1];
}
return true;
}
int main()
{
int ltotal, sum, flag;
Kruskal edge[MAX];
//scanf("%d", &ncase);
while(scanf("%d%d", &l, &v),l)
{
ltotal = 0, sum = 0, flag = 0;
for(int i = 1; i <= v; ++i) //初始化
{
father[i] = i;
son[i] = 1;
}
for(int i = 1; i <= l ; ++i)
{
scanf("%d%d%d", &edge[i].a, &edge[i].b, &edge[i].value);
}
sort(edge + 1, edge + 1 + l, cmp); //按权值由小到大排序
int count=0;
for(int i = 1; i <= l; ++i)
{
if(join(edge[i].a, edge[i].b))
{
ltotal++; //边数加1
sum += edge[i].value; //记录权值之和
//cout<<edge[i].a<<"->"<<edge[i].b<<endl;
}
if(ltotal == v - 1) //最小生成树条件:边数=顶点数-1
{
flag = 1;
break;
}
count++;
}
//printf("count%d\n", count);
if(flag)
printf("%d\n",sum);
else
printf("?\n");
}
return 0;
}
8、还差多少对v+v才能达到最小生成树
/*
目标是使全省任何两个城镇间都可以实现交通(但不一定有直接的道路相连,
只要互相间接通过道路可达即可)。问最少还需要建设多少条道路?
v e
4 2
1 3
4 3
3 3
1 2
1 3
2 3
5 2
1 2
3 5
999 0
0
输出最少还需要建设的道路数目。
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int pre[1010];
int unionsearch(int root) //查找父节点+路径压缩(非递归)
{
int son, tmp;
son = root;
while(root != pre[root])
root = pre[root];
while(son != root)
{
tmp = pre[son];
pre[son] = root;
son = tmp;
}
return root;
}
/*int unionsearch(int root) //查找父节点+路径压缩(递归)
{
return root == pre[root] ? root : unionsearch(pre[root]);
}*/
int main()
{
int num, road, total, i, start, end, root1, root2;
while(scanf("%d%d", &num, &road) && num)
{
total = num - 1;
for(i = 1; i <= num; ++i)
pre[i] = i;
while(road--)
{
scanf("%d%d", &start, &end);
root1 = unionsearch(start);
root2 = unionsearch(end);
if(root1 != root2)
{
pre[root1] = root2;
total--;
}
}
printf("%d\n", total);
}
return 0;
}
9、先判断是否畅通,再用最小生成树求最小花费
/*
对每个测试用例,在 1 行里输出全省畅通需要的最低成本。
若统计数据不足以保证畅通,则输出 No solution。
2
e v
3 3
1 2 1
1 3 2
2 3 4
1 3
2 3 2
样例输出
3
No solution
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
//判断是否畅通
int pre[1010];
int unionsearch(int root) //查找父节点+路径压缩(非递归)
{
int son, tmp;
son = root;
while(root != pre[root])
root = pre[root];
while(son != root)
{
tmp = pre[son];
pre[son] = root;
son = tmp;
}
return root;
}
//最小生成树
#define MAX 505
#define MAXCOST 0x7fffffff
int graph[MAX][MAX];
int prim(int graph[][MAX], int n)
{
int lowcost[MAX];
int mst[MAX];
int i, j, min, minid, sum = 0;
for (i = 2; i <= n; i++)
{
lowcost[i] = graph[1][i];
mst[i] = 1;
}
mst[1] = 0;
for (i = 2; i <= n; i++)
{
min = MAXCOST;
minid = 0;
for (j = 2; j <= n; j++)
{
if (lowcost[j] < min && lowcost[j] != 0)
{
min = lowcost[j];
minid = j;
}
}
//cout << "V" << mst[minid] << "-V" << minid << "=" << min << endl;
sum += min;
lowcost[minid] = 0;
for (j = 2; j <= n; j++)
{
if (graph[minid][j] < lowcost[j])
{
lowcost[j] = graph[minid][j];
mst[j] = minid;
}
}
}
return sum;
}
int main()
{
int num, road, total, start, end, root1, root2;
int i, j, k, m, n;
int x, y, cost;
int zushu;
cin>>zushu;
while(zushu--)
{
cin >> n >> m ;//m=顶点的个数,n=边的个数
num=m,road=n;
for (i = 1; i <= m; i++)
{
for (j = 1; j <= m; j++)
{
graph[i][j] = MAXCOST;
}
}
//------------判断是否畅通
//num顶点,road道路
total = num - 1;
for(i = 1; i <= num; ++i)
pre[i] = i;
for (k = 1; k <= n; k++)
{
cin >> i >> j >> cost;
graph[i][j] = cost;
graph[j][i] = cost;
start=i;
end=j;
root1 = unionsearch(start);
root2 = unionsearch(end);
if(root1 != root2)
{
pre[root1] = root2;
total--;
}
}
//cout<<total<<endl;
if(total<1)
{
cost = prim(graph, m);
printf("%d\n",cost);
}
else{
printf("No solution\n");
}
}
return 0;
}
10、dfs 欧拉回路
/*
2
v e
4 3
1 2
1 3
1 4
一笔画就yes,不一笔画no
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <climits>
#include <algorithm>
#include <cmath>
#define LL long long
using namespace std;
vector<int>e[1010];
int deg[1010];
bool vis[1010];
void dfs(int u) {
vis[u] = true;
for(int i = 0; i < e[u].size(); i++) {
if(!vis[e[u][i]]) {
dfs(e[u][i]);
}
}
}
int main() {
int kase,p,q,i,u,v,temp;
bool flag;
scanf("%d",&kase);
while(kase--) {
scanf("%d %d",&p,&q);
memset(deg,0,sizeof(deg));
memset(vis,false,sizeof(vis));
for(i = 0; i < 1010; i++)
e[i].clear();
for(i = 0; i < q; i++) {
scanf("%d %d",&u,&v);
e[u].push_back(v);
e[v].push_back(u);
deg[u]++;
deg[v]++;
}
dfs(1);
flag = true;
for(i = 1; i <= p; i++)
if(!vis[i]) {
flag = false;
break;
}
if(flag) {
for(temp = 0,i = 1; i <= p; i++) {
if(deg[i]&1) {
temp++;
if(temp > 2) break;
}
}
}
if(!flag || temp > 2) puts("No");
else puts("Yes");
}
return 0;
}
12、次小生成树(v+v+value型最小花费方案个数)
/*N个不同的城市里,这些城市分别编号1~N。
如果存在两种以上的最小花费方案则输出Yes,如果最小花费的方案只有一种,则输出No)
样例输入
2(组数)
3(v) 3(e)
v+v+value
1 2 1
2 3 2
3 1 3
4 4
1 2 2
2 3 2
3 4 2
4 1 2
样例输出
No
Yes
*/
#include<iostream>
#include<stdio.h>
#include<string>
#include<cstring>
#include<algorithm>
using namespace std;
#define MaxV 510
#define MaxE 200005
struct Edge
{
int x,y,dis;
};
Edge edge[MaxE],edge1[MaxE]; //分别是边数组 最小生成树中的边数组
int father[MaxV],Num[MaxV],num,dex,dey; //并查集 num统计生成树边的条数 dex dey指枚举删除边的x,y坐标
void Init(int V) //并查集初始化,单个元素自成集合
{
for(int i=1;i<=V;i++)
{
father[i]=i;
Num[i]=1;
}
}
int findfather(int x) //寻找父结点,可以压缩路径。。
{
for(x;x!=father[x];x=father[x]) ;
return father[x];
}
void Union(int x,int y)
{
int t1,t2;
t1=findfather(x);
t2=findfather(y);
if(Num[t1]>Num[t2])
{
father[t2]=t1;
Num[t1]+=Num[t2];
}
else
{
father[t1]=t2;
Num[t2]+=Num[t1];
}
}
int comp(const void* p1,const void* p2)
{
return (*(Edge*)p1).dis>(*(Edge*)p2).dis;
}
int Krusual(int V,int E)
{
int sum=0;
for(int i=1;i<=E;i++)
{
if(findfather(edge[i].x)!=findfather(edge[i].y))
{
sum+=edge[i].dis;
Union(edge[i].x,edge[i].y);
edge1[++num]=edge[i];
}
}
return sum;
}
int AKrusual(int V,int E)
{
Init(V);
int sum=0;
qsort(edge+1,E,sizeof(edge[1]),comp);
int k;
for(k=1;k<=E;++k)
{
if(findfather(edge[k].x)!=findfather(edge[k].y))
{
if(edge[k].x==dex&&edge[k].y==dey)
{continue;}
if(edge[k].x==dey&&edge[k].x==dex)
{continue;}
sum+=edge[k].dis;
Union(edge[k].x,edge[k].y);
}
}
return sum;
}
bool Judge(int V) //判断图是否连通,不连通则无法构造最小生成树
{
for(int m=1;m<=V-1;++m)
if(findfather(m)!=findfather(m+1))
return false;
return true;
}
int main()
{
int test,j,V,E;
scanf("%d",&test);
while(test--)
{
scanf("%d%d",&V,&E);
num=0;
for(j=1;j<=E;++j)
{
scanf("%d%d%d",&edge[j].x,&edge[j].y,&edge[j].dis);
}
qsort(edge+1,E,sizeof(edge[1]),comp);
Init(V);
int sum=Krusual(V,E);
int M=1000,temp;
for(int q=1;q<=num;++q)
{
dex=edge1[q].x;
dey=edge1[q].y;
temp=AKrusual(V,E);
if(temp<M&&Judge(V))
M=temp;
if(M==sum) break;
}
if(M==sum) printf("Yes\n");
else printf("No\n");
}
}
/*
13、判断最小生成树是否唯一
构成原最小生成树的边,一条边一条边的删。
*/
#include"stdio.h"
#include"stdlib.h"
struct A
{
int a,b;
int flag;
int len;
}eage[5555];
int n,m;
int set[111];
int cmp(const void *a,const void *b)
{
struct A *c,*d;
c=(struct A *)a;
d=(struct A *)b;
return c->len-d->len;
}
void build(int num)
{
int i;
for(i=1;i<=num;i++) set[i]=i;
}
int find(int k)
{
if(set[k]==k) return k;
set[k]=find(set[k]);
return set[k];
}
void Union(int f1,int f2)
{
set[f1]=f2;
}
int Kruskal(int t_d)
{
int i;
int ans;
int f1,f2;
int count=1;
ans=0;
for(i=0;i<m;i++)
{
if(count==n) break;
if(i==t_d) continue;
f1=find(eage[i].a);
f2=find(eage[i].b);
if(f1==f2) continue;
Union(f1,f2);
eage[i].flag=1;
ans+=eage[i].len;
count++;
}
return ans;
}
int main()
{
int T;
int z,i;
int a,b,c;
int ans,t_ans;
int del[111],k;
int flag;
int temp;
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
//m e n v
build(n);
for(i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
eage[i].a=a;
eage[i].b=b;
eage[i].len=c;
eage[i].flag=0;
}
qsort(eage,m,sizeof(eage[0]),cmp);
ans=Kruskal(-1);
temp=0;
for(i=1;i<=n;i++) if(set[i]==i) temp++;
if(temp>1) {printf("0\n");continue;}
k=0;
for(i=0;i<m;i++) if(eage[i].flag) del[k++]=i;
flag=0;
for(z=0;z<k;z++)
{
build(n);
t_ans=Kruskal(del[z]);
temp=0;
for(i=1;i<=n;i++) if(set[i]==i) temp++;
if(temp>1) continue;
if(t_ans==ans) {flag=1;break;}
}
if(flag) printf("Not Unique!\n");
else printf("%d\n",ans);
}
return 0;
}
经典问题:对于每一条边存在两个权值,分别是花费和长度,要生成一个树,使得总的花费比上总的长度最小。
13、最优比率生成树 (最优比例生成树)
/*
4(v)
x y z
0 0 0
0 1 1
1 1 2
1 0 3
0
cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差
dist[i][j]=dist[j][i]=sqrt(t);//距离为 点与点的水平距离
output: the minimum ratio of overall cost of the channels to the total length
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
#define N 1010
#define MAX 999999999
const double eps=1e-4;
int n;
int vis[N],x[N],y[N],z[N],pre[N];
double dis[N],cost[N][N],dist[N][N];
double prim(double x){
double totalcost=0,totaldist=0;
for(int i=1;i<=n;i++){
pre[i]=1;
}
dis[1]=0;
memset(vis,0,sizeof(vis));
vis[1]=1;
for(int i=2;i<=n;i++){
dis[i]=cost[1][i]-dist[1][i]*x;
}
int k;
for(int i=2;i<=n;i++){
double mincost=MAX;
for(int j=2;j<=n;j++){
if(!vis[j]&&dis[j]<mincost){
mincost=dis[j];
k=j;
}
}
vis[k]=1;
totalcost+=cost[pre[k]][k];
totaldist+=dist[pre[k]][k];
for(int j=1;j<=n;j++){
if(!vis[j]&&dis[j]>cost[k][j]-dist[k][j]*x){
dis[j]=cost[k][j]-dist[k][j]*x;
pre[j]=k;
}
}
}
return totalcost/totaldist;
}
int main(){
while(scanf("%d",&n),n){
for(int i=1;i<=n;i++){
scanf("%d%d%d",&x[i],&y[i],&z[i]);
for(int j=1;j<i;j++){
double t=(x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]);
cost[i][j]=cost[j][i]=abs(z[i]-z[j]);//花费为海拔之差
dist[i][j]=dist[j][i]=sqrt(t);//距离为 点与点的水平距离
}
}
double a=0;
while(1){
double b=prim(a);
if(abs(a-b)<eps)break;
else a=b;
//cout<<a<<endl;
}
printf("%.3f\n",a);
}
return 0;
}
14、判断边与一个图最小生成shu的关系
题意:给出一张带权无向图,然后询问该图的每条边进行询问,若这条边出现在该图的所有最小生成树中,输出any;若这条边可以出现在这张图的某几个最小生成树中,输出at least once;若这条边不会出现在这张图的任意一个最小生成树中,输出none。
/*
input
4 5
1 2 101
1 3 100
2 3 2
2 4 2
3 4 1
output
none
any
at least one
at least one
any
*/
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define maxn 100010
using namespace std;
struct Edge{
int from,to,dist,id;
}a[maxn];
int first[maxn],v[maxn*2],next[maxn*2],id[maxn*2];
int ans[maxn],fa[maxn],pre[maxn],dfs_clock,e;
int cmp(Edge a,Edge b){
return a.dist < b.dist;
}
void init(){
e = 0;
memset(first,-1,sizeof(first));
}
void add_edge(int a,int b,int ID){
v[e] = b;
next[e] = first[a];
id[e] = ID;
first[a] = e++;
}
int dfs(int u,int fa_id){
int lowu = pre[u] = ++dfs_clock;
for(int i = first[u];i != -1;i = next[i]){
if(!pre[v[i]]){
int lowv = dfs(v[i],id[i]);
lowu = min(lowu,lowv);
if(lowv > pre[u]) ans[id[i]] = 1;
}else if(pre[v[i]] < pre[u] && id[i] != fa_id){
lowu = min(lowu,pre[v[i]]);
}
}
return lowu;
}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
void join(int x,int y){
int fx = find(x);
int fy = find(y);
if(fx != fy){
e = 0;
first[fx] = first[fy] = -1;
pre[fx] = pre[fy] = 0;
fa[fx] = fy;
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
init();
for(int i = 1;i <= n;i++) fa[i] = i;
dfs_clock = 0;
for(int i = 0;i < m;i++){
scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].dist);
a[i].id = i;
}
sort(a,a+m,cmp);
memset(pre,0,sizeof(pre));
memset(ans,0,sizeof(ans));
for(int i = 0;i < m;i++){
int j = i+1;
while(j < m && a[i].dist == a[j].dist) j++;
for(int k = i;k < j;k++){
int fx = find(a[k].from);
int fy = find(a[k].to);
if(fx != fy){
add_edge(fx,fy,a[k].id);
add_edge(fy,fx,a[k].id);
ans[a[k].id] = 2;
}
}
for(int k = i;k < j;k++){
int fx = find(a[k].from);
int fy = find(a[k].to);
if(fx != fy && !pre[fx]){
dfs(fx,-1);
}
}
for(int k = i;k < j;k++){
join(a[k].from,a[k].to);
}
i = j - 1;
}
for(int i = 0;i < m;i++){
if(ans[i] == 0) printf("none\n");
else if(ans[i] == 1) printf("any\n");
else printf("at least one\n");
}
return 0;
}
15、hdu 3371 最小生成树(有重边)
/*
the cities are signed from 1 to n.
1
6( survived cities) 4(you can choose to connect the cities ) 3(still connected cities)
it takes c to connect p and q
p q c
1 4 2
2 6 1
2 3 5
3 4 33
the number of this connected citie(2ge 2ge 3ge)
2 1 2
2 1 3
3 4 5 6
Output
For each case, output the least money you need to take, if it’s impossible, just output -1.
*/
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int n,m,k,p[505],ans;
struct node{
int u,v,w;
}pt[250000];
int cmp(node a,node b)
{
return a.w<b.w;
}
int find(int x){return p[x]==x?x:p[x]=find(p[x]);}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int i,j,num,a,b,root,x,y;
for(i=0;i<=500;i++) p[i]=i;
bool flag=false;
scanf("%d%d%d",&n,&m,&k);
for(i=1;i<=m;i++)scanf("%d%d%d",&pt[i].u,&pt[i].v,&pt[i].w);
sort(pt+1,pt+m+1,cmp);
for(i=1;i<=k;i++)
{
scanf("%d%d",&num,&root);
for(j=1;j<num;j++)
{
scanf("%d",&a);
x=find(a),y=find(root);
if(x!=y) p[x]=y;
}
}
for(num=0,i=1;i<=n;i++) if(p[i]==i) num++;
for(ans=0,i=1;i<=m;i++)
{
x=find(pt[i].u);y=find(pt[i].v);
if(x!=y)
{
p[x]=y;
ans+=pt[i].w;num--;
}
if(num==1)
{
flag=true;
break;
}
}
if(flag) printf("%d\n",ans);
else printf("-1\n");
}
return 0;
}