【NOIP2013模拟】四叶草魔杖
Time Limits: 1000 ms Memory Limits: 131072 KB
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,∑ni=1Ai=0。
解题思路
我们可以找到几个集合,每个集合点的权值和为0。
那么我们可以将每个集合求出最小生成树的距离,标记到一个数组f里,用二进制状态压缩储存,对于每两个编号,x,y,用f[x]+f[y]更新f[x xor y],存入x xor y。
var father:array[0..16]of shortint;
f:array[0..65536] of longint;
v:array[0..16]of longint;
a:array[0..1000,1..3]of longint;
t:array[0..1000000]of longint;
i,n,m,top,j:longint;
function get(x:longint):longint;
begin
if father[x]=x then exit(x);
father[x]:=get(father[x]);
exit(father[x]);
end;
procedure qsort(h,t:longint);
var l,r,m:longint;
begin
l:=h; r:=t;
m:=a[(h+t)div 2,3];
repeat
while a[l,3]<m do inc(l);
while a[r,3]>m do dec(r);
if l<=r then
begin
a[0]:=a[l]; a[l]:=a[r]; a[r]:=a[0];
inc(l); dec(r);
end;
until l>r;
if h<r then qsort(h,r);
if l<t then qsort(l,t);
end;
function check(what,tot:longint):longint;
var o,x,y,num,dis:longint;
begin
if tot=1 then exit(0);
for o:=1 to n do father[o]:=o;
num:=0; dis:=0;
for o:=1 to m do //做最小生成树
begin
if ((1 shl (a[o,1]-1))and what)>0 then
if ((1 shl (a[o,2]-1))and what)>0 then
begin
x:=get(a[o,1]); y:=get(a[o,2]);
if x<>y then
begin
father[x]:=father[y];
inc(num); inc(dis,a[o,3]);
if num=tot-1 then exit(dis);
end;
end;
end;
exit(maxlongint);
end;
procedure find(x,tot,what,sum:longint); //寻找权值和为0的点集
var o:longint;
begin
if sum=0 then
begin
f[what]:=check(what,tot); //检查集合是否连通
if f[what]<maxlongint then
begin
inc(top);
t[top]:=what;
end;
end;
if x>n then exit;
for o:=x to n do find(o+1,tot+1,what+1 shl (o-1),sum+v[o]); //参数传递用二进制状态压缩
end;
procedure print;
var x,y:longint;
begin
i:=0;
while i<top do
begin
inc(i);
for j:=i+1 to top do
begin
x:=f[t[i]]+f[t[j]]; //更新
y:=t[i] xor t[j];
if x<f[y] then
begin
inc(top);
t[top]:=y; f[y]:=x; //存入队列
end;
end;
end;
if f[1 shl n-1]=maxlongint then writeln('Impossible')else writeln(f[1 shl n-1]);
end;
begin
read(n,m);
for i:=1 to n do read(v[i]);
for i:=1 to m do
begin
read(a[i,1],a[i,2],a[i,3]);
inc(a[i,1]); inc(a[i,2]);
end;
qsort(1,m);
for i:=0 to 1 shl n do f[i]:=maxlongint;
find(1,0,0,0);
print;
end.