1 条题解

  • 0
    @ 2025-8-24 23:02:50

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Polarisx
    一位努力不落后的 NPC

    搬运于2025-08-24 23:02:50,当前版本为作者最后更新于2025-08-18 21:18:49,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题目传送门

    紫题认真的吗?

    思路

    对于一个点集 SS,要让它能量归零当且仅当这个点集的 aia_i 和为 00,最小代价显然是该点集导出子图的最小生成树边权之和,容易发现最终选择的边一定是由若干个最小生成树拼凑而成的,再结合数据范围,我们可以直接上子集 DP,时间复杂度 O(2nn2logm+3n)\mathcal O(2^nn^2\log m+3^n)

    可以用子集 exp 优化到 O(2nn2logm+2nn2)\mathcal O(2^nn^2\log m+2^nn^2),不过没什么必要。

    #include <bits/stdc++.h>
    #define ll long long 
    using namespace std;
    
    const int Maxn=110;
    const ll inf=1e15;
    int n,m;
    int a[Maxn];
    int G[Maxn][Maxn];
    ll g[1<<16|5];
    
    struct edg{
        int u,v,w;
    }E[Maxn];
    int tot,f[Maxn];
    
    int qfind(int key){
        return f[key]==key?key:f[key]=qfind(f[key]);
    }
    
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=0;i<n;i++) scanf("%d",&a[i]);
    
        memset(G,-1,sizeof G);
        for(int i=0;i<m;i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            G[u][v]=w;
        }
        for(int i=0;i<(1<<n);i++){
            tot=0; ll sm=0;
            for(int j=0;j<n;j++) f[j]=j;
            for(int j=0;j<n;j++) if(i>>j&1) sm+=a[j];
            if(sm!=0){
                g[i]=inf;
                continue;
            }
            for(int j=0;j<n;j++)
                for(int k=0;k<n;k++)
                    if(G[j][k]!=-1 and i>>j&1 and i>>k&1)
                        E[++tot]=(edg){j,k,G[j][k]};
            sort(E+1,E+tot+1,[](edg x,edg y){
                return x.w<y.w;
            });
            ll ret=0; int cnt=0;
            for(int j=1;j<=tot;j++){
                int u=qfind(E[j].u),v=qfind(E[j].v);
                if(qfind(u)!=qfind(v)){
                    f[v]=u; ++cnt;
                    ret+=E[j].w;
                }
            }
            int p=__builtin_popcount(i); 
            
            if(cnt==p-1) g[i]=ret; else g[i]=inf;
        }
        for(int i=0;i<(1<<n);i++)
            for(int j=i;j;j=i&(j-1))
                g[i]=min(g[i],g[i^j]+g[j]);
    
        ll ans=g[(1<<n)-1];
        if(ans==inf){
            puts("Impossible");
            return 0;
        }
        printf("%lld",ans);
        
    
        return 0;
    }
    
    • 1

    信息

    ID
    10145
    时间
    1000ms
    内存
    512MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者