首页 > 代码库 > vijos 天空
vijos 天空
背景 Background
小杉坐在教室里,透过口袋一样的窗户看口袋一样的天空。
有很多云飘在那里,看起来很漂亮,小杉想摘下那样美的几朵云,做成棉花糖。
描述 Description
给你云朵的个数N,再给你M个关系,表示哪些云朵可以连在一起。
现在小杉要把所有云朵连成K个棉花糖,一个棉花糖最少要用掉一朵云,小杉想知道他怎么连,花费的代价最小。
输入格式 Input Format
每组测试数据的
第一行有三个数N,M,K(1<=N<=1000,1<=M<=10000,1<=K<=10)
接下来M个数每行三个数X,Y,L,表示X云和Y云可以通过L的代价连在一起。(1<=X,Y<=N,0<=L<10000)
30%的数据N<=100,M<=1000
输出格式 Output Format
对每组数据输出一行,仅有一个整数,表示最小的代价。
如果怎么连都连不出K个棉花糖,请输出‘No Answer‘。
样例输入 Sample Input
3 1 2
1 2 1
样例输出 Sample Output
1
时间限制 Time Limitatio
每个测试点1s
注释 Hint
样例2:
Input:
3 1 1
1 2 1
Output:
No Answer
分析:
这个题其实很简单,就是尽量生成最小生成树(用克鲁斯卡尔算法,PS:传说中的kruskal,先排序,后贪心的算法),有可能图不是连通的,也就是,给的数据是好几个图,这样,我们将要求的最小生成树的个数设为k,我们生成的最小生成树的个数是m,若m=k,则直接输出代价就可以了,如果m>k则说明图的个数比要求的最小生成树个数小,怎么也无法构成k个最小生成树,也就是输出"No Answer"
如果m<k说明,求出的最小生成树个数比要求的最小生成树少,这里,我们可以用剪断枝的方法来将一棵树砍成两棵(PS:刚才是剪,现在怎么砍了?),此时,用贪心将用过的边最长的砍掉,然后,判断砍断后的树的数量是否与要求的数量相同,如果不相同就再砍掉当前的最长边,直到现在树的数目与所求数目相同时就输出,当然,还存在着如果把所有的用到的枝都砍掉还是不能凑够k时再输出"No Answer"
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<string>
using namespace std;
int n,m,k,set[110],cost[110],used[110];
bool flag;
struct line
{
int x,y,c;
}list[110];
bool cmp(line l,line t)
{
return l.c<t.c;
}
int find(int x)
{
if(set[x]!=x) set[x]=find(set[x]);
return set[x];
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=m;i++)
scanf("%d%d%d",&list[i].x,&list[i].y,&list[i].c);
sort(list+1,list+1+m,cmp);
for(int i=1;i<=n;i++)
set[i]=i,cost[i]=0;
for(int i=1;i<=m;i++)
{
int x,y;
x=find(list[i].x);
y=find(list[i].y);
if(x!=y)
{
set[x]=y;
used[i]=true;
cost[x]+=cost[y]+list[i].c;
}
}
int maxx=0;
for(int i=1;i<=n;i++)
if(set[i]==i)
maxx++;
if(maxx>k)
printf("NO ANSWER\n");
else
{
int sum=0;
for(int i=1;i<=m;i++)
sum+=cost[i];
if(maxx==k)
printf("%d\n",sum);
else
{
for(int i=m;i>=1;i--)
{
if(used[i])
{
sum=sum-list[i].c;
maxx++;
if(maxx==k)
{
flag=true;
break;
}
}
}
if(flag) printf("NO ANSWER\n");
else printf("%d\n",sum);
}
}
return 0;
}
vijos 天空