Live2D

【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;
}
posted @ 2021-07-06 20:01  冷笑叹秋萧  阅读(84)  评论(0编辑  收藏  举报