口胡笔记

NKOJ 2439 四叶草魔杖 \(\color{CornflowerBlue}{【状压dp】}\)

枚举点集,若当前点集构成连通块且能量之和为 \(0\),显然当前连通块传递能量的最小代价是其最小生成树,因此将每个这样的连通块看成一个物品,背包 \(dp\) 算出最小费用即可。

//https://blog.csdn.net/Mogician_Evian/article/details/77840301
#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];
}

猜你喜欢

转载自www.cnblogs.com/milky-w/p/9761752.html