【NOIP2013模擬】四葉草魔杖 題解
【NOIP2013模擬】四葉草魔杖
Description
魔杖護法Freda融合了四件武器,於是魔杖頂端緩緩地生出了一棵四葉草,四片葉子煥發著淡淡的七色光。聖劍護法rainbow取出了一個圓盤,圓盤上鑲嵌著N顆寶石,編號為0~N-1。第i顆寶石的能量是Ai。如果Ai>0,表示這顆寶石能量過高,需要把Ai的能量傳給其他寶石;如果Ai<0,表示這顆寶石的能量過低,需要從其他寶石處獲取-Ai的能量。保證sigma(Ai)=0。只有當所有寶石的能量均相同時,把四葉草魔杖插入圓盤中央,才能開啟超自然之界的通道。
不過,只有M對寶石之間可以互相傳遞能量,其中第i對寶石之間無論傳遞多少能量,都要花費Ti的代價。探險隊員們想知道,最少需要花費多少代價才能使所有寶石的能量都相同?
Input
第一行兩個整數N、M。
第二行N個整數Ai。
接下來M行每行三個整數pi,qi,Ti,表示在編號pi和qi的寶石之間傳遞能量需要花費Ti的代價。資料保證每對pi、qi最多出現一次。
Output
輸出一個整數表示答案。無解輸出Impossible。
Sample Input
3 3
50 -20 -30
0 1 10
1 2 20
0 2 100
Sample Output
30
Data Constraint
對於50%的資料,2<=N<=8。
對於100%的資料,2<=N<16,0<=M<=N*(N-1)/2,0<=pi,qi<N,-1000<=Ai<=1000,0<=Ti<=1000,sigma(Ai)=0。
題解
看到資料範圍,果斷狀壓,可惜考場思路假了(一天一道狀壓DP啊)
感性地YY一下題目,發現這題可能跟最小生成樹有點兒關係,於是我們可以順著這個思路來想
先設狀態,設\(f[s]表示\)使狀態\(s\)中為1的點中全部傳遞成0需要的最小代價
然後我們考慮轉移,實際上我們可以列舉兩個集合狀態\(s\)和\(s'\)來進行合併轉移
我們先將每一個狀態\(s\)所需要的代價算出來,不難發現其實就是這個連通塊的最小生成樹的邊權之和
然後轉移方程就一目瞭然了:
\(f[s|s']=min(f[s|s'],f[s]+形成狀態s'所需的代價)\)
CODE
#include<cstdio> #include<string> #include<cstring> #include<algorithm> #define max(a,b) (((a)>(b))?(a):(b)) #define min(a,b) (((a)<(b))?(a):(b)) #define R register int #define N 20 #define M 100005 #define inf 0x3f3f3f3f #define ll long long using namespace std; struct arr{int x,y,z;}bian[M]; int n,m,tot,sum[M],num[M],fa[N],a[N],f[M]; inline void read(int &x) { x=0;int f=1;char ch=getchar(); while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();} while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48),ch=getchar();x*=f; } inline int find(int k) { if (fa[k]==k) return k;else return fa[k]=find(fa[k]); } inline void kruskal(int k) { if (sum[k]) {num[k]=inf;return;} int cnt=1,ans=0,tot1=0; for (R i=1;i<=n;++i) if (k&(1<<i-1)) ++tot1,fa[i]=i; for (R i=1;i<=m;++i) { int xx=bian[i].x,yy=bian[i].y; if (k&(1<<xx-1) && k&(1<<yy-1)) { int f1=find(xx),f2=find(yy); if (f1!=f2) { fa[f1]=f2;++cnt;ans+=bian[i].z; if (cnt==tot1) break; } } } if (cnt==tot1) num[k]=ans;else num[k]=inf; } inline bool cmp(arr x,arr y) {return x.z<y.z;} int main() { read(n);read(m);tot=(1<<n)-1; for (R i=1;i<=n;++i) read(a[i]); for (R i=1;i<=m;++i) { read(bian[i].x);read(bian[i].y);read(bian[i].z); ++bian[i].x;++bian[i].y; } sort(bian+1,bian+1+m,cmp); for (R s=0;s<=tot;++s) { for (R i=1;i<=n;++i) if (s&(1<<i-1)) sum[s]+=a[i]; kruskal(s);f[s]=inf; } f[0]=0; for (R s=0;s<=tot;++s) if (!sum[s]) for (R i=0;i<=tot;++i) if (!sum[i]) f[s|i]=min(f[s|i],f[s]+num[i]); if (f[tot]==inf) printf("Impossible");else printf("%d\n",f[tot]); return 0; }