• 2018"百度之星"程序设计大赛


    problem

    • 给一张n个点m条边的有向图,每条边有一个正整数权值以及一种色光三原色红、绿、蓝之一的颜色。
    • 恰好选出k条边,满足只用这k条边之中的红色边和绿色边(或者蓝色边和绿色边)就能使n个点之间两两连通
    • 对于k==1…m,计算选出恰好k条满足条件的边的权值之和的最小值。

    solution

    • 当 k < n-1 时,分别求两种情况的最小生成树(红绿,蓝绿),记录每种情况用了哪些边。
    • 当 k > n-1 时,按照边权从小到大找没有用过的直接加

    codes

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int maxn = 1100, inf=2147000000;
    
    //Grape
    int n, m;
    struct Edge{ int u, v, w; char c; }e[maxn];
    bool cmp(Edge a, Edge b){return a.w<b.w;}
    
    //UnionFindSet
    int fa[maxn];
    void init(int _n){for(int i = 1; i <= _n; i++)fa[i]=i;}
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    void merge(int x, int y){x=find(x),y=find(y);fa[x]=y;}
    
    //MST
    int vis[3][maxn];//记录边
    int solve(int s){
        memset(vis[s],0,sizeof(vis[s]));
        int mst = 0;
        init(n);
        for(int i = 1; i <= m; i++){
            if(s==1&&e[i].c=='B')continue;
            if(s==2&&e[i].c=='R')continue;
            int x = find(e[i].u), y = find(e[i].v);
            if(x != y){
                merge(x,y);
                mst += e[i].w;
                vis[s][i] = 1;
            }
        }
        for(int i = 2; i <= n; i++)//判断是否联通
            if(find(i)!=find(1))return -1;
        return mst;
    }
    
    //Timu
    int ans1[maxn], ans2[maxn];
    
    int main(){
        int w;  scanf("%d",&w);
        for(int _w = 1; _w <= w; _w++){
            printf("Case #%d:
    ", _w);
            memset(ans1,-1,sizeof(ans1));
            memset(ans2,-1,sizeof(ans2));
    
            scanf("%d%d",&n,&m);
            for(int i = 1; i <= m; i++)
                scanf("%d%d%d %c",&e[i].u,&e[i].v,&e[i].w,&e[i].c);
            sort(e+1,e+m+1,cmp);
    
            for(int i = 1; i < n-1; i++)
                printf("-1
    ");
            int mst1 = solve(1);
            int mst2 = solve(2);
            if(mst1==-1&&mst2==-1){//森林,无法联通
                for(int i = n; i <= m; i++)
                    printf("-1
    ");
                continue;
            }
            int cur = n-1;
            if(mst1==-1){//赋初始值
                ans1[cur] = inf;
                ans2[cur] = mst2;
            }else if(mst2==-1){
                ans1[cur] = mst1;
                ans2[cur] = inf;
            }else{
                ans1[cur] = mst1;
                ans2[cur] = mst2;
            }
            if(mst1!=-1){
                for(int i = 1; i <= m; i++)
                    if(!vis[1][i]){cur++;ans1[cur]=ans1[cur-1]+e[i].w;}
            }else for(int i = n; i <= m; i++)ans1[i] = inf;
            cur = n-1;
            if(mst2!=-1){
                for(int i = 1; i <= m; i++)
                    if(!vis[2][i]){cur++;ans2[cur]=ans2[cur-1]+e[i].w;}
            }else for(int i = n; i <= m; i++)ans2[i] = inf;
    
            for(int i = n-1; i <= m; i++)
                printf("%d
    ",min(ans1[i],ans2[i]));
        }
        return 0;
    }
    
  • 相关阅读:
    live2d 快速实现好看的看板娘特效
    JQuery 日期转换日期方法封装
    SQL Server 之 DateTime的常用方法
    C# 之DateDiff 时间差扩展方法
    SQL Server 之如何查询某数据库下的触发器和语句
    Css 设置固定表格头部,内容可滚动
    jquery 点击tr选中checkbox,解决checkbox的默认点击事件被阻止的问题
    VS切换代码自动补全模式
    C#实现软键盘的几个关键技术介绍
    C# 模拟软件键盘输入,使Winfrom窗体不获取鼠标焦点方法
  • 原文地址:https://www.cnblogs.com/gwj1314/p/9444602.html
Copyright © 2020-2023  润新知