题目链接
一.题目内容
给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;
}
}
四.小结
有时想不出来好的方案可以根据数据猜做法