四叶草魔杖

链接

第一眼是不会的,想了一会才有思路,而且还是瞄了一眼了别人的题解的。
我的想法最开始就是普通的最小生成树,然后对于0的节点就枚举是否要取它,然后跑\(2^n\)遍就好了。
但是有一个问题,就是,这整张图是可以不连通的。也就是我们可以只把它变成几个内部点权相加为0的连通块。
然后我就有点不会了。

其实\(n\leq 16\),明显的状压。
但是就算知道是状压与不方便,因为对于做法还是没有什么提示的。

其实想法也不难。
我们就对于每一组点权相加为0的节点组尝试在里面做最小生成树,如果能就记录下来,不行就标记个-1。
然后跑dp就好了,转移的时候就是尝试合并两个连通块,如果合并完能够覆盖所有有权值的节点就可以了。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
inline int read() {
	char c=getchar();int a=0,b=1;
	for(;c<'0'||c>'9';c=getchar())if(c=='-')b=-1;
	for(;c>='0'&&c<='9';c=getchar())a=a*10+c-48;return a*b;
}
struct edge
{
	int next,to,v;
}e[241];
int head[241],tot,n,m,a[17],ma[17][17];
int can[1000001];
inline void add(int i,int j,int v)
{
	e[++tot].next=head[i];
	e[tot].to=j;
	e[tot].v=v;
	head[i]=tot;
}
bool vis[17];
int d[17];
int prim(int k)
{
	memset(vis,0,sizeof(vis));
	memset(d,0x3f,sizeof(d));
	int root=0,ans=0,cnt=0;
	for(int i=0;i<n;i++)
	{
		if((k>>i)&1&&root==0)
		{
			root=i;
		}
		if((k>>i)&1)cnt++;
	}
	vis[root]=1;
	for(int i=head[root];i!=0;i=e[i].next)
	{
		int u=e[i].to;
		d[u]=min(d[u],e[i].v);
	}
//	cout<<root<<endl;
//	for(int i=0;i<n;i++)
//	{
//		cout<<d[i]<<' ';
//	}
//	cout<<endl;
	for(int i=1;i<cnt;i++)
	{
		int Min=0x3f3f3f3f,Minid=-1;
		for(int j=0;j<n;j++)
		{
			if(Min>d[j]&&((k>>(j))&1)&&vis[j]==0)
			{
//				cout<<j<<endl;
				Min=d[j];
				Minid=j;
			}
		}
		if(Minid==-1)
		{
//			cout<<i<<endl;
			return 0x3f3f3f3f;
		}
//		cout<<Min<<endl;
		vis[Minid]=1;
		ans+=Min;
		for(int j=head[Minid];j!=0;j=e[j].next)
		{
			int u=e[j].to;
			d[u]=min(d[u],e[j].v);
		}
	}
	return ans;
}
int f[70001];
int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	n=read(),m=read();int endans=0;
	for(int i=0;i<n;i++)
	{
		a[i]=read();
		if(a[i]==0)endans+=(1<<(i));
	}
	for(int i=1;i<=m;i++)
	{
		int x=read(),y=read(),z=read();
		ma[x][y]=ma[y][x]=z;
		add(x,y,z);add(y,x,z);
	}
	memset(can,0x3f,sizeof(can));
//	cout<<prim(7)<<endl;
//	return 0;
	for(int i=0;i<(1<<n);i++)
	{
		int sum=0;
		for(int j=0;j<n;j++)
		{
			if((i>>j)&1)
			{
				sum+=a[j];
			}
		}
		if(sum==0)
		{
			can[i]=prim(i);
//			cout<<can[i]<<endl;
		}
	}
	int ans=0x3f3f3f3f;
//	cout<<endans<<endl;
	for(int i=0;i<(1<<n);i++)
	{
		f[i]=can[i];
		for(int j=i;j!=0;j=((j-1)&(i)))
		{
			f[i]=min(f[i],f[(i^j)]+f[j]);
//			cout<<f[i]<<endl;
		}
		if((i|endans)==((1<<n)-1))
		{
//			cout<<"?"<<endl;
			ans=min(ans,f[i]);
		}
	}
	if(ans==0x3f3f3f3f)cout<<"Impossible"<<endl;
	else
	cout<<ans<<endl;
	return 0;
}

难得的真的一遍过的状压dp。
主要还是样例运气好吧。
这题要我说没有什么复杂麻烦的应用,就是正常的分析模型,然后按部就班的做。
练练手吧算是

posted @ 2024-03-17 21:33  HL_ZZP  阅读(8)  评论(0编辑  收藏  举报