1. 程式人生 > >TYVJ 2054 [Nescafé29]四葉草魔杖 最小生成樹 狀態壓縮/背包DP

TYVJ 2054 [Nescafé29]四葉草魔杖 最小生成樹 狀態壓縮/背包DP

這一 put struct 之間 body 就是 clas 相同 提示

$ \rightarrow $ 戳我進TYVJ原題

[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;
}

TYVJ 2054 [Nescafé29]四葉草魔杖 最小生成樹 狀態壓縮/背包DP