BZOJ 3590 Quare [状压DP][边双连通分量][点双联通分量]

30 篇文章 0 订阅
5 篇文章 0 订阅

题目传送门

题目意思就是在一个无向图内求一个权值最小的且包含所有点的双联通子图。
DP乱搞,拆边双联通成链,与下一个子问题。状态压缩选择边的情况。貌似写了很多无用的DP。。。毕竟最后AC时我都不明觉厉。。
这种DP+图论神题还是少做吧,要夭寿的。
推荐这篇blog

#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define clr(x) memset((x),0,sizeof(x))
#define clt(x) memset((x),0x1f,sizeof(x))
#define smin(x,y) (x)=min((x),(y))
#define smax(x,y) (x)=max((x),(y))
using namespace std;
const int N = 13;
const int M = 1<<13;
const int W = 50;
const int V = 1<<28;
int T,n,m;
struct data{
    int to,next,val;
}E[W<<2];
int head[M],tot;
int h[N][M][2],g[M][N][N],p[M][N][N],f[M];
void addedge(int u,int v,int z){
    E[++tot].to=v,E[tot].next=head[u],E[tot].val=z,head[u]=tot;
    E[++tot].to=u,E[tot].next=head[v],E[tot].val=z,head[v]=tot;
}
void init(){
    tot=0,clr(head),clr(h),clt(g),clr(p),clt(f);
}
bool DP_JU(int x,int y){
    return x>>(y-1)&1;
}
void DP_init(){
    for(register int i=1;i<=n;i++)
        for(int j=0;j<(1<<n);j++){
            int u=V,v=V;
            for(register int rt=head[i];rt;rt=E[rt].next)
                if((j>>(E[rt].to-1)&1))
                    if(E[rt].val<u)v=u,u=E[rt].val;
                    else smin(v,E[rt].val);
            h[i][j][0]=u,h[i][j][1]=v;
        }
    for(register int i=1;i<=n;i++)p[1<<(i-1)][i][i]=g[1<<(i-1)][i][i]=0;
}
void DP_FOR_CUT(){
    for(register int i=1;i<=n;i++)p[1<<(i-1)][i][i]=0;
    for(register int cut=1;cut<(1<<n);cut++)
        for(register int i=1;i<=n;i++)if(DP_JU(cut,i))
            for(register int j=1;j<=n;j++)if(DP_JU(cut,j)){
                for(int rt=head[i];rt;rt=E[rt].next)
                    if(DP_JU(cut,E[rt].to))
                        smax(p[cut&(1<<(E[rt].to-1))][E[rt].to][j],p[cut][i][j]+E[rt].val);
                for(int rt=head[j];rt;rt=E[rt].next)
                    if(DP_JU(cut,E[rt].to))
                        smax(p[cut&(1<<(E[rt].to-1))][i][E[rt].to],p[cut][i][j]+E[rt].val);
            }
}
void DP_FOR_SPLIT(){
    for(register int i=1;i<=n;i++)g[1<<(i-1)][i][i]=0;
    for(register int split=1;split<(1<<n);split++)
        for(register int i=1;i<=n;i++)if(DP_JU(split,i))
            for(register int j=1;j<=n;j++)if(DP_JU(split,j)){
                for(int rt=head[i];rt;rt=E[rt].next)
                    if(!DP_JU(split,E[rt].to))
                        smin(g[split|(1<<(E[rt].to-1))][E[rt].to][j],g[split][i][j]+E[rt].val);
                for(int rt=head[j];rt;rt=E[rt].next)
                    if(!DP_JU(split,E[rt].to))
                        smin(g[split|(1<<(E[rt].to-1))][i][E[rt].to],g[split][i][j]+E[rt].val);
            }
}
void DP_FOR_LINK(){
    for(register int i=1;i<=n;i++)f[1<<(i-1)]=0;
    for(register int link=1;link<(1<<n);link++){
        int u=((1<<n)-1)^link;
        for(register int k=u;k;k=(--k)&u)
            for(register int i=1;i<=n;i++)if(DP_JU(k,i))
                    for(register int j=1;j<=n;j++)if(DP_JU(k,j))
                        if(i^j)smax(f[link^k],f[link]+p[k][i][j]+h[i][link][0]+h[j][link][0]);
                        else   smax(f[link^k],f[link]+p[k][i][j]+h[i][link][0]+h[i][link][1]);
    }
}
void DP_FOR_MERGE(){
    for(register int i=1;i<=n;i++)f[1<<(i-1)]=0;
    for(register int merge=1;merge<(1<<n);merge++){
        int u=((1<<n)-1)^merge;
        for(register int k=u;k;k=(--k)&u)
            for(register int i=1;i<=n;i++)if(DP_JU(k,i))
                    for(register int j=1;j<=n;j++)if(DP_JU(k,j))
                        if(i^j)smin(f[merge^k],f[merge]+g[k][i][j]+h[i][merge][0]+h[j][merge][0]);
                        else   smin(f[merge^k],f[merge]+g[k][i][j]+h[i][merge][0]+h[i][merge][1]);

    }
}
inline void read(int &res){
    static char ch;int flag=1;
    while((ch=getchar())<'0'||ch>'9')if(ch=='-')flag=-1;res=ch-48;
    while((ch=getchar())>='0'&&ch<='9')res=res*10+ch-48;res*=flag;
}
int main(){
    read(T);
    while(T--){
        int x,y,z;init();
        read(n),read(m);
        for(register int i=1;i<=m;i++)
            read(x),read(y),read(z),addedge(x,y,z);
        DP_init();
        DP_FOR_CUT();
        DP_FOR_LINK();
        DP_FOR_SPLIT();
        DP_FOR_MERGE();
        if(f[(1<<n)-1]>=V)cout<<"impossible"<<endl;
        else cout<<f[(1<<n)-1]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值