四叶草魔杖
第一眼是不会的,想了一会才有思路,而且还是瞄了一眼了别人的题解的。
我的想法最开始就是普通的最小生成树,然后对于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。
主要还是样例运气好吧。
这题要我说没有什么复杂麻烦的应用,就是正常的分析模型,然后按部就班的做。
练练手吧算是