2012年湖南省第八届大学生程序设计大赛 F题 “Kingdoms ” CSG - 1062 (最小生成树,二进制枚举)

题目链接


一.题目内容

给n个点和m条边,每个点都有价值ai,每条边费用为bi,现在给你k费用,让你建一个图,求该图中与1直接或间接相连的点的总价值最大为多少

样例解释

Input
2
4 6 6
500 400 300 200
1 2 4
1 3 3
1 4 2
4 3 5
2 4 6
3 2 7
4 6 5
500 400 300 200
1 2 4
1 3 3
1 4 2
4 3 5
2 4 6
3 2 7

Output
1100
1000

  • 样例一 :建边1和边3,1-2,1-4 花费6,总价值为1100最大
  • 样例二 :建边2和边3,1-3,1-4 花费5,总价值为1000最大

二.解题思路

题目数据给的很有启发性,n最大为16。由于情况太多不好选择建图方案,所以可以直接枚举所有情况,最多也就只有2^16,数的二进制每一位对应的点标号,1表示建立,0表示没有该点。

再将只含枚举点的边取出来,做最小生成树,求出建好该图的最小费用,然后和所给费用k比较,若小于等于则可行,将获得的价值与结果进行比较更新。


题目要求的是与1直接或间接相连的点,所以最开始1必在所建图中,最后得出的最小生成树的点必等于边+1,不然则不是树,不全部相连。


三.解题代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pa pair<int,int>
#define rep(i,j,k) for(int i=j;i<=k;i++)
#define rrep(i,j,k) for(int i=j;i>=k;i--)
const int N=20;
int po[N];
int n,m,k;
struct node {
    int u,v,w;
    bool operator < (const node &temp) {
        return w<temp.w;
    }
};
int a[N];
int fi(int x) {
    return a[x]==x?x:a[x]=fi(a[x]);
}
bool check(int si,vector<node>eg) {
    sort(eg.begin(),eg.end());
    for(int i=1; i<=n; i++) a[i]=i;
    int co=0,cnt=0;
    for(auto e:eg) {
        if(co+e.w>k) break; //若所需费用大于k
        int x=fi(e.v),y=fi(e.u); //最小生出树并查集操作
        if(x!=y){
            a[x]=y; 
            co+=e.w; //加边
            if(++cnt+1==si) return 1; //若边数+1等于点数,代表建立成功,返回1
        }
    }
    return 0;
}
signed main() {
    int t;
    cin>>t;
    while(t--) {
        cin>>n>>m>>k;
        rep(i,1,n) cin>>po[i];
        vector<node>edge(m+1);
        while(m--) {
            int u,v,w;
            cin>>u>>v>>w;
            edge.push_back(node{u,v,w});
        }
        int ans=po[1];
        for(int i=1; i<(1<<(n-1)); i++) { //枚举
            int temp=po[1];
            unordered_map<int,int>mk;
            mk[1]=1;// 点1必在
            for(int j=2; j<=n; j++) {
                if(i&(1<<(j-2))) temp+=po[j],mk[j]=1; //标记枚举到的点
            }
            vector<node>e;
            set<int>s;
            for(auto j:edge) {
                if(mk.find(j.u)!=mk.end()&&mk.find(j.v)!=mk.end()) {
                    e.push_back(j); //添加只含枚举点的边
                    s.insert(j.u);
                    s.insert(j.v);
                }
            }
            if(s.size()!=mk.size()) continue; 
            //若所添加的边里含的点数量不等于要建的点数量,则肯定不能建成全部相连的图
            if(check(s.size(),e)) ans=max(ans,temp); 
        }
        cout<<ans<<endl;
    }
}




四.小结

有时想不出来好的方案可以根据数据猜做法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值