BZOJ题号3624
题目描述
输入描述
输出描述
输入样例
5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1
输出样例
3 2 0
4 3 0
5 3 1
1 2 1
试题大意
给出n个村庄,m条路,其中有0种路和1种路,要求村庄互相连通,保存道路最少,而且保存k条0种路。
输出保存的路径。
①算法确定
看到这道题我就窃喜,果然是大水题!
这道题有两个条件:在所有的村庄都连通时保存的免费道路最少,保存的石子路为K条。要最少,最少就是n-1条路,很明显就是最小生成树。但是问题在于:我们要求k条0种边。这种删边题目,要么是最小生成树,要么是最小割。
②算法细则
我们不能知道最后的结果,但是我们来一步一步看:
A.有没有一些0种边,必须要选择,如果不选择就不能联通?
这些边是我们答案的一部分(如果有部分分那就太好了)
这些边其实很容易求:先用最小生成树算法,用1种边连接,这之后有一些点已经联通了,但是还有一些点没有联通。剩下的这些点就不得不用0种边联通了。到此为止还是最小生成树,如果完全连成一个图,那么边数是n-1。
(因为这里求的是最少的边数,所以每条边的权为1。那么这个最小生成树的贪心法则直接就是能让一个新的点进入联通图中,就是最优的。这一点不证自明。)
B.接下来怎么做呢?
我们现在得到了一些必须要选的0种边。我们把图中的边清空,先把必须要加上的0种边加上去。这种时候,既然要K条0种边,我就用最小生成树把0种边加到K条。如果现在还不行,就加1种边。这样为什么是最优的?看下面:
加必须的0种边(不加不能联通)
0种边加到K(不加不是我们的答案)
加1种边(不加你能干什么)
所以这样就得出了我们的结果。
C.有什么可以优化呢?
1、如果求必须要加的边时,必须加的边超过K,或者必须加的0种边和已经加的1种边和不为n-1(得不到联通),直接输出无解。
2、如果最后得到的图中,0种边加不到K,输出无解。(不一定是总的0种边没有K条,而是加到K条不是最小生成树)
代码
不给你看
#include<cstdio>
#include<cstdlib>
#include<cstring>
const int M=100005;
const int N=20005;
int n,m,k,top;
struct qq{
int x,y;
bool stone;
}ans[M],a[M];
int num[2];
int fa[N];
bool tf[M];
int find(int x)
{
if(fa[x]==x)return fa[x];
else fa[x]=find(fa[x]);
return fa[x];
}
void done(bool type,int maxx)
{
for(int i=1;i<=m;i++)
{
if(a[i].stone==type && num[type]<maxx)
{
int fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy)
{
fa[fx]=fy;
ans[++top].x=a[i].x;ans[top].y=a[i].y;ans[top].stone=a[i].stone;
tf[i]=true;
num[type]++;
}
}
}
}
int main()
{
num[0]=num[1]=top=0;
scanf("%d %d %d",&n,&m,&k);
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].stone);
}
done(1,999999999);
done(0,999999999);
if(num[0]+num[1]!=n-1 || num[0]>k)
{
printf("no solution\n");
return 0;
}
num[0]=num[1]=top=0;
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=m;i++)
{
if(a[i].stone==0 && tf[i]==true)
{
int fx=find(a[i].x),fy=find(a[i].y);
if(fx!=fy)
{
fa[fx]=fy;
ans[++top].x=a[i].x;ans[top].y=a[i].y;ans[top].stone=a[i].stone;
num[0]++;
}
}
}
done(0,k);
done(1,99999999);
if(num[0]<k)
{
printf("no solution\n");
return 0;
}
for(int i=1;i<=top;i++)
printf("%d %d %d\n",ans[i].x,ans[i].y,ans[i].stone);
return 0;
}