HDU 6349 三原色图(最小生成树)
题意
n个点,m条边。每条边有三种颜色,红绿蓝。现在问只取1-m条边,只用红色边和绿色边或者只用蓝色边和绿色边就能使 n 个点之间两两连通,输出最小权值,够不成则输出-1。
解题思路
跑两遍最小生成树,对于够不成最小生成树的情况,肯定全输出-1,对于能构成最小生成树的情况,最小生成树的边数量一定是n-1,那么剩下的[n,m]区间内就取不在最小生成树上的最小权值边,依次递推过去即可,也不用关心它是什么颜色的边。取两种情况对应边数的最小值,即为结果。
太久没写过图论的题了,代码写的有点挫。。
代码
#include<bits/stdc++.h>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 1e2+50;
struct node
{
int x,y,val;
char color;
} arr[maxn];
int flag[maxn];
int cmp(node a,node b)
{
return a.val<b.val;
}
int n,m,p[maxn];
int finds(int x)
{
return p[x]==x?x:p[x]=finds(p[x]);
}
int kruskal(int c)
{
memset(flag,0,sizeof flag);
for(int i=1; i<=n; i++)
p[i]=i;
int ans=0,cnt=0;
for(int i=0; i<m; i++)
{
if(c)
{
if(arr[i].color=='B') continue;
int x=finds(arr[i].x);
int y=finds(arr[i].y);
if(x!=y)
{
flag[i]=1;
ans+=arr[i].val;
p[x]=y;
cnt++;
}
if(cnt==n-1) return ans;
}
else
{
if(arr[i].color=='R') continue;
int x=finds(arr[i].x);
int y=finds(arr[i].y);
if(x!=y)
{
flag[i]=1;
ans+=arr[i].val;
p[x]=y;
cnt++;
}
if(cnt==n-1) return ans;
}
}
return inf;
}
int v1[maxn],v2[maxn];
int main()
{
#ifdef DEBUG
freopen("in.txt","r",stdin);
#endif // DEBUG
int t;
scanf("%d",&t);
for(int _=1; _<=t; _++)
{
scanf("%d%d",&n,&m);
printf("Case #%d:\n",_);
for(int i=1; i<n-1; i++) printf("-1\n");
for(int i=0; i<m; i++)
{
scanf("%d%d%d",&arr[i].x,&arr[i].y,&arr[i].val);
getchar();
arr[i].color=getchar();
getchar();
}
sort(arr,arr+m,cmp);
memset(v1,-1,sizeof v1);
memset(v2,-1,sizeof v2);
v1[n-1]=kruskal(1);//红绿
if(v1[n-1]==inf) for(int i=n-1; i<=m; i++) v1[i]=inf;
else
{
int cur=n-1;//边数
for(int i=0; i<m; i++)
{
if(flag[i]) continue;
else
{
cur++;
v1[cur]=v1[cur-1]+arr[i].val;
}
if(cur==m) break;
}
}
v2[n-1]=kruskal(0);//蓝红
if(v2[n-1]==inf) for(int i=n-1; i<=m; i++) v2[i]=inf;
else
{
int cur=n-1;//边数
for(int i=0; i<m; i++)
{
if(flag[i]) continue;
else
{
cur++;
v2[cur]=v2[cur-1]+arr[i].val;
}
if(cur==m) break;
}
}
for(int i=n-1; i<=m; i++)
v1[i]=min(v1[i],v2[i]);
for(int i=n-1; i<=m; i++)
if(v1[i]==inf) printf("-1\n");
else printf("%d\n",v1[i]);
}
return 0;
}