[Apio2008]免费道路[Kruscal]

3624: [Apio2008]免费道路

Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special Judge
Submit: 1292  Solved: 518
[Submit][Status][Discuss]

Description

Input

Output

Sample Input

5 7 2
1 3 0
4 5 1
3 2 0
5 3 1
4 3 0
1 2 1
4 2 1

Sample Output

3 2 0
4 3 0
5 3 1
1 2 1

HINT

Source

自己的第一次思路:
  小数据枚举k条鹅卵石路,大数据随机找k条鹅卵石路,跟剩下的水泥路构树,如果可以构成树,则此方案可行。
  随机化的阈值我设置的是20.

  然后就砍到72分。

#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=2e4+5;
const int M=1e5+5;
struct edge{int u,v,w,id;}e[M],z[M];
int n,m,num0,K,tot,cct,fa[N],ans[M];
int a[200];bool vis[200];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
inline bool cmp(const edge &a,const edge &b){
    return a.id<b.id;
}
inline bool cmp2(const edge &a,const edge &b){
    return a.w<b.w;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void pre(){
    tot=0;ans[0]=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    random_shuffle(e+1,e+num0+1);
    for(int i=1,x,y;i<=num0;i++){
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;ans[++ans[0]]=i;
            if(++tot==K) break;
        }
    }
}
inline void ord(){
    tot=0;ans[0]=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1,x,y;i<=K;i++){
        x=find(e[a[i]].u);y=find(e[a[i]].v);
        if(x!=y){
            fa[y]=x;ans[++ans[0]]=a[i];
            if(++tot==K) break;
        }
    }
}
inline void work(){
    for(int i=num0+1,x,y;i<=m;i++){
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;ans[++ans[0]]=i;
            if(++tot==n-1) break;
        }
    }
}
inline void print(){
    for(int i=1;i<=ans[0];i++) z[i]=e[ans[i]];
    sort(z+1,z+ans[0]+1,cmp);
    for(int i=1;i<=ans[0];i++) printf("%d %d %d\n",z[i].u,z[i].v,z[i].w);
}
void dfs(int x){
    if(x>K){
        ord();work();
        if(tot==n-1){print();exit(0);}
        return ;
    }
    for(int i=a[x-1]+1;i<=num0;i++){
        if(!vis[i]){
            vis[i]=1;
            a[x]=i;
            if(num0-i<K-x) break;
            dfs(x+1);
            vis[i]=0;
            a[x]=0;
        }
    }
}
int main(){
//    freopen("sh.txt","r",stdin);
    srand(time(0));
    n=read();m=read();K=read();
    for(int i=1;i<=m;i++){
        e[i].u=read(),e[i].v=read(),e[i].w=read(),e[i].id=i;
        if(!e[i].w) num0++;
    }
    sort(e+1,e+m+1,cmp2);
    if(K<=10){dfs(1);puts("no solution");return 0;}
    while(1){
        pre();
        if(tot!=n-1) work();
        if(tot==n-1){print();return 0;}
        if(++cct==20) break;
    }
    puts("no solution");
//    cnt=ans[0];ans[0]=0;
//    for(int i=1;i<=ans[0];i++) printf("%d %d %d\n",e[ans[i]].u,e[ans[i]].v,e[ans[i]].w);
    /*for(int i=1;i<=cnt;i++) z[i]=e[ans[i]];
    sort(z+1,z+cnt+1,cmp2);
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1,tot=0,x,y;i<=cnt;i++){
        x=find(z[i].u);y=find(z[i].v);
        if(x!=y){
            fa[y]=x;ans[++ans[0]]=i;
            if(++tot==n-1) break;
        }
    }*/
    /*cnt=ans[0];ans[0]=0;
    for(int i=1;i<=cnt;i++) e[i]=z[ans[i]];
    sort(e+1,e+cnt+1,cmp);*/
//    for(int i=1;i<=cnt;i++) printf("%d %d %d\n",e[i].u,e[i].v,e[i].w);
    return 0;
}

代码留念

自己的第二次思路:(后悔当时为什么没有多想想)
  2次kruscal解决。
  第一次kruscal:首先考虑把所有水泥路连上。如果构不成树,则需要用鹅卵石路填边,这些鹅卵石路是必须要的鹅卵石路(如果必须要的鹅卵石路的数量>K直接无解);剩下的鹅卵石路都是不必要的。
  第二次kruscal:先把上一次找到的必须要的鹅卵石路填上,此时的鹅卵石路不一定恰有K条,可能比K条少,于是考虑优先连不必要的鹅卵石路,直到凑满K条为止,然后剩下的边随便找几条水泥路连起来就好(special judge告诉我们输出任意解均可)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=4e4+5;
const int M=2e5+5;
struct edge{int u,v,w,tag;}e[M];
int n,m,K,tot,fa[N];
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)f=-1;ch=getchar();}
    while(ch>=‘0‘&&ch<=‘9‘){x=x*10+ch-‘0‘;ch=getchar();}
    return x*f;
}
int find(int x){
    return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void ReadData(){
    n=read();m=read();K=read();
    for(int i=1;i<=m;i++) e[i].u=read(),e[i].v=read(),e[i].w=read(),e[i].tag=0;
}
inline void Kruscal1(){
    int cct=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1,x,y;i<=m;i++) if(e[i].w){//先铺水泥路
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;
            if(++cct==n-1) break;
        }
    }
    if(cct==n-1) return ;
    for(int i=1,x,y;i<=m;i++) if(!e[i].w){
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;tot++;e[i].tag=1;//必要鹅卵石
            if(++cct==n-1) break;
        }
    }
}
inline void Kruscal2(){
    int cct=0;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1,x,y;i<=m;i++) if(e[i].tag){//整理找出的必要鹅卵石
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;
            if(++cct==n-1) break;
        }
    }
    for(int i=1,x,y;i<=m;i++) if(!e[i].w){
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;++cct;e[i].tag=1;//不必要鹅卵石补齐K条
            if(++tot==K) break;
        }
    }
    if(tot!=K){puts("no solution");exit(0);}
    for(int i=1,x,y;i<=m;i++) if(e[i].w){//水泥路补齐n-1条
        x=find(e[i].u);y=find(e[i].v);
        if(x!=y){
            fa[y]=x;e[i].tag=1;
            if(++cct==n-1) break;
        }
    }
}
inline void WriteAns(){
    for(int i=1;i<=m;i++) if(e[i].tag) printf("%d %d %d\n",e[i].u,e[i].v,e[i].w);
}
int main(){
    ReadData();
    Kruscal1();
    Kruscal2();
    WriteAns();
    return 0;
}
时间: 2024-11-05 06:40:48

[Apio2008]免费道路[Kruscal]的相关文章

bzoj 3624: [Apio2008]免费道路 生成树的构造

3624: [Apio2008]免费道路 Time Limit: 2 Sec  Memory Limit: 128 MBSec  Special JudgeSubmit: 111  Solved: 49[Submit][Status] Description Input Output Sample Input 5 7 2 1 3 0 4 5 1 3 2 0 5 3 1 4 3 0 1 2 1 4 2 1 Sample Output 3 2 0 4 3 0 5 3 1 1 2 1 还是看的网上的标

3624: [Apio2008]免费道路

3624: [Apio2008]免费道路 https://www.lydsy.com/JudgeOnline/problem.php?id=3624 题意: 一张无向图,每种边有两种类型0和1.求一个最小生成树使得有k条0边. 分析: 为了满足有k条0边的限制,先考虑0边哪些必选,如果所有1边都加入后,还有0边可以使得图不连通,那么这些0边必须选. 把必须选的加上然后再加到k,然后再加1边.中间判一下是否必选的大于k,0边是否大于等于k. 代码: 1 #include<bits/stdc++.h

[APIO2008]免费道路

# [APIO2008]免费道路 ### 第一反应 考虑朴素的克鲁斯卡尔算法加一个限制,先选鹅卵石路,且选到k个就停止 带来的问题: - ~~叶子节点特殊处理,都选上~~(但其实是连通性) - ~~而且你诡异的发现,tm,这个鹅卵石路可以突破最小生成树!!!~~(不仔细看题面的后果) ### 正解 考虑上文中的连通性,先用水泥路跑一遍$Kruskal$,然后不连通的且用到鹅卵石路的都要选上.剩下的既然水泥路可以,那么鹅卵石路也可以代替嘛,先选鹅卵石路,选到$k$个就停止 emm,那么什么时候是无

bzoj3624: [Apio2008]免费道路

具体题目:https://vjudge.net/problem/HYSBZ-3624 Description 一个王国有N个城市和M条无向道路,这M条道路中有一些是鹅卵石路一些是水泥路.现在国王要选择尽可能少的路免费,并且使每两个城市都有一条免费路径.国王打算保留刚好K条鹅卵石路.请问国王是否可以办到所有这些要求,如果能则输出路径的两个城市和路的种类,否则"no solution". HINTN <= 20000M <= 100000 Analysis如果能成功肯定是最小生

【bzoj3624】Apio2008—免费道路

http://www.lydsy.com/JudgeOnline/problem.php?id=3624 (题目链接) 题意 给出一张无向图,其中有0类边和1类边.问能否构成正好有K条0类边的生成树,并输出方案. Solution 先将所有1类边加入生成树,然后再加入0类边,那么现在加入的0类边就是必须加入的0类边,将它们打上标记.然后再将并查集初始化,继续加0类边直到数量达到K,最后加1类边. 代码 // bzoj3624 #include<algorithm> #include<io

BZOJ 3624: [Apio2008]免费道路 [生成树 并查集]

题意: 一张图0,1两种边,构造一个恰有k条0边的生成树 优先选择1边构造生成树,看看0边是否小于k 然后保留这些0边,补齐k条,再加1边一定能构成生成树 类似kruskal的证明 #include <iostream> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=2e4+5, M=1e5+5; typedef long

【[APIO2008]免费道路】

\(kruskal\)好题 \(0\)边的数量在某些情况下是可以无限制的调控的,前提是所有必须存在的边都在生成树里了 所以应该分别求出有哪些边是必须在生成树里的,我们可以先从大到小排序,求出有哪些\(0\)边必须在生成树里,之后再从小到大排序,求出那些\(1\)边必须在生成树里 之后剩下的边就可以随便放了,调控\(0\)边的个数恰好为\(k\)即可 代码 #include<iostream> #include<algorithm> #include<cstring> #

APIO2008免费道路

题目大意 给定一张n个点m条边的图,图上有两种边,求保证有k条第一种边的情况下的最小生成树 传送门 题解 考虑最小生成树kruskal算法 先找到不含限制的最小生成树,然后就可以知道哪些第一种边是必选的 然后跑第二遍kruskal,先把第一种边加到k条,然后加入第二种边就好 代码 #include<bits/stdc++.h> #define inf 1000000000 #define ll long long using namespace std; int read() { int x=

BZOJ 3624 免费道路

第一反应:这不先0后1做并查集就行了吗? 然后WA了... 哦....啊?哦...233 如果按顺序做并查集,有些0的边可能很重要(只能由它作为0连起两个联通块),但并没有被选. 于是先按1做并查集,选出这些边,再按0,1做并查集. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 20050 #define maxe 100500 u