枚举点集,若当前点集构成联通块且能量之和为0,显然当前联通块传递能量的最小代价是其最小生成树,因此将每个这样的联通块看成一个物品,背包dp算出最小费用即可。
#include<bits/stdc++.h>
using namespace std;
struct oppo {
int f,t,s;
} rood[100000];
int n,m;
int a[20];
int sum[100000];
int v_v[100000];
int dp[100000];
bool rule(oppo a,oppo b) {
return a.s<b.s;
}
int fa[20];
int find(int x) {
return fa[x]==x?x:fa[x]=find(fa[x]);
}
int O_O(int x) {
int tot=0,ans=0,all=0;
for(int i=1; i<=n; i++) {
if(x&(1<<(i-1)))
tot++;
fa[i]=i;
}
for(int i=1; i<=m; i++) {
if(x&(1<<(rood[i].f))&&x&(1<<(rood[i].t))) {
if(find(rood[i].f+1)!=find(rood[i].t+1)) {
fa[find(rood[i].f+1)]=find(rood[i].t+1);
ans+=rood[i].s;
all++;
}
}
}
if(all==tot-1||tot==1)
return ans;
return -1;
}
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++)
cin>>a[i];
for(int i=1; i<=m; i++)
scanf("%d%d%d",&rood[i].f,&rood[i].t,&rood[i].s);
sort(rood+1,rood+m+1,rule);
int MAX=(1<<n)-1;
for(int i=0; i<=MAX; i++)
for(int j=0; j<n; j++)
if(i&(1<<j))
sum[i]+=a[j+1];
for(int i=0; i<=MAX; i++) {
if(!sum[i])
v_v[i]=O_O(i);
else
v_v[i]=-1;
}
memset(dp,10,sizeof(dp));
dp[0]=0;
for(int i=0; i<=MAX; i++)
if(!sum[i])
for(int j=0; j<=MAX; j++)
if((i|j)==i&&v_v[i-j]!=-1)
dp[i]=min(dp[i],dp[j]+v_v[i-j]);
if(dp[MAX]==dp[MAX+1])
puts("Impossible");
else
cout<<dp[MAX];
return 0;
}
16 1
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 1 0
这样就T掉了
直接枚举子集不行的吧,复杂度不对啊