NKOJ 2439 四葉草魔杖(最小生成樹+狀壓dp/網路流)
阿新 • • 發佈:2019-02-09
2439 四葉草魔杖
問題描述
魔杖護法Freda融合了四件武器,於是魔杖頂端緩緩地生出了一棵四葉草,四片葉子幻發著淡淡的七色光。聖劍護法rainbow取出了一個圓盤,圓盤上鑲嵌著N顆寶石,編號為0~N-1。第i顆寶石的能量是Ai。如果Ai>0,表示這顆寶石能量過高,需要把Ai的能量傳給其它寶石;如果Ai<0,表示這顆寶石的能量過低,需要從其它寶石處獲取-Ai的能量。保證∑Ai =0。只有當所有寶石的能量均相同時,把四葉草魔杖插入圓盤中央,才能開啟超自然之界的通道。
不過,只有M對寶石之間可以互相傳遞能量,其中第i對寶石之間無論傳遞多少能量,都要花費Ti的代價。探險隊員們想知道,最少需要花費多少代價才能使所有寶石的能量都相同?
輸入格式
第一行兩個整數N、M。
第二行N個整數Ai。
接下來M行每行三個整數pi,qi,Ti,表示在編號為pi和qi的寶石之間傳遞能量需要花費Ti的代價。資料保證每對pi、qi最多出現一次。
輸出格式
輸出一個整數表示答案。無解輸出Impossible
樣例輸入
3 3
50 -20 -30
0 1 10
1 2 20
0 2 100
樣例輸出
30
提示
對於 50% 的資料,2<=N<=8。 對於 100% 的資料,2<=N<=16,0<=M<=N*(N-1)/2,0<=pi,qi<N,-1000<=Ai<=1000,0<=Ti<=1000,∑Ai=0。
做法一:最小生成樹+狀壓dp
列舉點集,若當前點集構成聯通塊且能量之和為0,顯然當前聯通塊傳遞能量的最小代價是其最小生成樹,因此將每個這樣的聯通塊看成一個物品,揹包dp算出最小費用即可。
程式碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
struct node{int x,y,z;};
bool cmp(node a,node b)
{return a.z<b.z;}
int n,m,A[20 ],S[66666],V[66666],F[66666],TOT;
int fa[20];
bool mark[20];
node P[200];
int GF(int x)
{
if(fa[x]!=x)fa[x]=GF(fa[x]);
return fa[x];
}
int Kruscal(int s)
{
int i,j,fx,fy,x,y,k=1,tot=0,cnt=0,ans=0;
memset(mark,0,sizeof(mark));
for(i=1;i<=n;i++)if((1<<i-1)&s)tot++,mark[i]=1;
for(i=1;i<=n;i++)fa[i]=i;
while(k<=m&&cnt<tot)
{
x=P[k].x;
y=P[k].y;
fx=GF(x);fy=GF(y);
if(mark[x]&&mark[y]&&fx!=fy)
{
ans+=P[k].z;
fa[fx]=fy;
cnt++;
}
k++;
}
if(cnt+1<tot)return 1e9;
return ans;
}
int main()
{
int i,j;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)scanf("%d",&A[i]);
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&P[i].x,&P[i].y,&P[i].z);
P[i].x++;P[i].y++;
}
sort(P+1,P+m+1,cmp);
TOT=(1<<n)-1;
for(i=1;i<=TOT;i++)
for(j=1;j<=n;j++)if((1<<j-1)&i)S[i]+=A[j];
for(i=1;i<=TOT;i++)if(S[i]==0)V[i]=Kruscal(i);
for(i=1;i<=TOT;i++)F[i]=1e9;F[0]=0;
for(i=0;i<=TOT;i++)
{
if(S[i])continue;
for(j=0;j<=TOT;j++)F[i|j]=min(F[i|j],F[j]+V[i]);
}
if(F[TOT]==1e9)puts("Impossible");
else cout<<F[TOT];
}
做法二:網路流
建圖比較簡單,從源點向每個
程式碼:
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
#include<deque>
#define N 123
#define M 1234
using namespace std;
int qg=0,n,m,S,T,A[123];
int TOT=1,LA[N],EN[M],NE[M],G[M],W[M],F[M];
int dis[N],use[N],pre[N],maxflow,mincost;
bool mark[N];
queue<int>Q;
void ADD(int x,int y,int w,int c)
{
TOT++;
EN[TOT]=y;
G[TOT]=w;
F[TOT]=c;
W[TOT]=c;
NE[TOT]=LA[x];
LA[x]=TOT;
}
bool FP()
{
int i,x,y;
memset(dis,60,sizeof(dis));
dis[S]=0;mark[S]=1;Q.push(S);
while(!Q.empty())
{
x=Q.front();
Q.pop();
mark[x]=0;
for(i=LA[x];i;i=NE[i])
{
y=EN[i];
if(G[i]&&dis[y]>dis[x]+W[i])
{
dis[y]=dis[x]+W[i];
pre[y]=x;use[y]=i;
if(!mark[y])mark[y]=1,Q.push(y);
}
}
}
if(dis[T]!=dis[0])return 1;
return 0;
}
void AF()
{
int f=1e9,i;
for(i=T;i!=S;i=pre[i])f=min(f,G[use[i]]);
maxflow+=f;
for(i=T;i!=S;i=pre[i])
{
G[use[i]]-=f;
G[use[i]^1]+=f;
if(G[use[i]]!=1e9)W[use[i]^1]=W[use[i]]=0;
else
{
W[use[i]]=F[use[i]];
W[use[i]^1]=F[use[i]^1];
}
}
}
int main()
{
int i,x,y,z;
scanf("%d%d",&n,&m);
S=n+1;T=S+1;
for(i=1;i<=n;i++)
{
scanf("%d",&A[i]);
if(A[i]>0)
{
ADD(S,i,A[i],0);
ADD(i,S,0,0);
qg+=A[i];
}
else
{
ADD(i,T,-A[i],0);
ADD(T,i,0,0);
}
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
x++;y++;
ADD(x,y,1e9,z);
ADD(y,x,1e9,z);
}
while(FP())AF();
if(maxflow!=qg)puts("Impossible");
else
{
for(i=2;i<=TOT;i++)
if(G[i]&&G[i]!=1e9)mincost+=F[i];
cout<<mincost/2;
}
}