TYVJ 2054 [Nescafé29]四叶草魔杖 最小生成树 状态压缩/背包DP
[Nescafé29]四叶草魔杖
题目限制
时间限制 | 内存限制 | 评测方式 | 题目来源 |
---|---|---|---|
1000ms | 131072KiB | 标准比较器 | Local |
题目背景
陶醉在彩虹光芒笼罩的美景之中,探险队员们不知不觉已经穿过了七色虹,
到达了目的地,面前出现了一座城堡和小溪田园,城堡前的木牌上写着“Poetic Island”。
“这一定就是另外两位护法的所在地了……我们快进去吧!”
探险队员们快步进入了城堡,城堡大厅的羊毛沙发上坐着两个人。
“你们是Nescafe的护法吧?”
“是的哦~ 我们就是圣剑护法rainbow和魔杖护法freda~ 你们来这里做什么呢~”
“我们是来拜访圣主和四位护法的……”
“可是圣主applepi已经前往超自然之界的学校(Preternatural Kingdom University,简称PKU)修炼魔法了,
要想见到他,必须开启Nescafe之塔与超自然之界的通道。但是圣主规定,开启通道的方法不能告诉任何外人。
我只能提示你们,开启通道的钥匙就与四位护法有关T_T”
探险队员环视四周,突然,其中一人的目光停留在了魔杖之上。
“hoho~ 魔杖!传说中开启异时空通道的钥匙不就叫四叶草魔杖吗?
四叶草有力量、信心、希望和幸运四片叶子,护法恰好有神刀、飞箭、圣剑、魔杖四位!aha~我找到答案了!”
“好吧,那我们就满足你们的愿望~”
题目描述
魔杖护法Freda融合了四件武器,于是魔杖顶端缓缓地生出了一棵四叶草,四片叶子幻发着淡淡的七色光。
圣剑护法rainbow取出了一个圆盘,圆盘上镶嵌着 $ N $ 颗宝石,编号为 $ 0~N-1 $ 。
第i颗宝石的能量是 $ A_i $ 。
如果 $ A_i>0 $ ,表示这颗宝石能量过高,需要把Ai的能量传给其它宝石;
如果 $ A_i<0 $ ,表示这颗宝石的能量过低,需要从其它宝石处获取 $ -A_i $ 的能量。
保证 $ ∑A_i =0 $ 。只有当所有宝石的能量均相同时,把四叶草魔杖插入圆盘中央,才能开启超自然之界的通道。
不过,只有 $ M $ 对宝石之间可以互相传递能量,其中第 $ i $ 对宝石之间无论传递多少能量,都要花费 $ T_i $ 的代价。
探险队员们想知道,最少需要花费多少代价才能使所有宝石的能量都相同?
输入格式
第一行两个整数 $ N、M $ 。
第二行 $ N $ 个整数 $ A_i $ 。
接下来 $ M $ 行每行三个整数 $ p_i,q_i,T_i $ ,表示在编号为 $ p_i $ 和 $ q_i $ 的宝石之间传递能量需要花费 $ T_i $ 的代价。
数据保证每对 $ p_i、q_i $ 最多出现一次。
输出格式
输出一个整数表示答案。无解输出Impossible。
提示
对于 $ 50 $ % 的数据,$ 2 \le N \le 8 $ 。
对于 $ 100 $ % 的数据,$ 2 \le N \le 16,0 \le M \le N*(N-1)/2,0 \le p_i,q_i<N,-1000 \le A_i \le 1000,0 \le T_i \le 1000,∑A_i=0 $ 。
样例数据
输入样例
3 3
50 -20 -30
0 1 10
1 2 20
0 2 100
输出样例
30
题解
-
每个 $ \sum A_i =0 $ 的子图内传递能量的最小代价是其最小生成树
-
最终整个图可能分成若干块 $ \sum a_i =0 $ 的子图分别传递
-
以 $ \sum a_i =0 $ 的子图为物品做二进制集合背包的状态压缩 $ DP $
代码
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define inf 1000000007
struct edge{ int u,v,w; }e[205];
int n,m,a[20],f[1<<20],g[1<<20],fa[20],tot;
bool vis[20];
bool cmp(edge x,edge y){ return x.w<y.w; }
int calc(int x){
int res=0;
for(int i=1;i<=n;++i) if(x&(1<<i-1)) res+=a[i];
return res;
}
int find(int x){
if(fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
int kruskal(int x){
int num=0,res=0,tmp=0;
memset(vis,0,sizeof(vis));
for(int i=1;i<=n;++i){
fa[i]=i;
if(x&(1<<(i-1))) vis[i]=1,++num;
}
for(int fu,fv,i=1;i<=m;++i){
if(!vis[e[i].u]||!vis[e[i].v]) continue;
fu=find(e[i].u); fv=find(e[i].v);
if(fu==fv) continue;
fa[fu]=fv;
res+=e[i].w;
++tmp; if(tot==num-1) break;
}
if(tmp!=num-1) return inf;
return res;
}
int main(){
scanf("%d %d",&n,&m);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=m;++i){
scanf("%d %d %d",&e[i].u,&e[i].v,&e[i].w);
++e[i].u; ++e[i].v;
}
sort(e+1,e+1+m,cmp);
memset(f,0x3f,sizeof(f));
f[0]=0;
for(int i=1;i<=(1<<n)-1;++i)
if(calc(i)==0){
int tmp=kruskal(i);
if(tmp!=inf) g[++tot]=i; f[i]=tmp;
}
for(int i=0;i<=(1<<n)-1;++i)
for(int j=1;j<=tot;++j)
if((i&g[j])==0) f[i|g[j]]=min(f[i|g[j]],f[i]+f[g[j]]);
if(f[(1<<n)-1]==inf) puts("Impossible");
else printf("%d",f[(1<<n)-1]);
return 0;
}