【模板】【斯坦纳树】HDU 4085

1 篇文章 0 订阅

//hdu 4085 斯坦纳树模板题
const int maxn=55;
const int maxm=2005;
const int maxstate=1<<11;

int T;
int n,m,k;
int dp[maxn][maxstate];
int st[maxn];
int dp2[maxstate];

struct node {
    int v,w,next;
};
node edges[maxm];
int tot;
int maps[maxn];
void addedge(int u,int v,int w) {
    edges[tot].v=v;
    edges[tot].w=w;
    edges[tot].next=maps[u];
    maps[u]=tot;
    tot++;
}

queue<int>que;
int in[maxn];
void spfa(int sta) {
    while(!que.empty()) {
        int now=que.front();
        que.pop();
        in[now]=0;
        for(int i=maps[now]; i!=-1; i=edges[i].next) {
            int v=edges[i].v;
            int w=edges[i].w;
            if(dp[v][sta]>dp[now][sta]+w) {
                dp[v][sta]=dp[now][sta]+w;
                if(in[v]==0) {
                    in[v]=1;
                    que.push(v);
                }
            }
        }
    }
}
bool check(int sta) {
    int res=0;
    for(int i=0; sta; i++) {
        if(sta&1) {
            if(i<k)
                res++;
            else
                res--;
        }
        sta>>=1;
    }
    return (res==0);
}

void init() {
    //初始化链式前向星
    mst(maps,-1);
    tot=0;
    //初始化spfa的队列和队内元素标记
    while(!que.empty())
        que.pop();
    mst(in,0);
}

void solve() {
    sd(T);
    while(T--) {
        init();
        sddd(n,m,k);
        //建图
        for(int i=0; i<m; i++) {
            int u,v,w;
            sddd(u,v,w);
            addedge(u,v,w);
            addedge(v,u,w);
        }

        //给每个需要联通的点一个状压位
        mst(st,0);
        mst(dp,INF);
        int nowst=1;
        for(int i=1; i<=k; i++) {
            st[i]=nowst;
            dp[i][st[i]]=0;
            nowst<<=1;
        }
        for(int i=n-k+1; i<=n; i++) {
            st[i]=nowst;
            dp[i][st[i]]=0;
            nowst<<=1;
        }

        //核心
        int mxst=1<<(2*k);
        for(int s=0; s<mxst; s++) {
            for(int i=1; i<=n; i++) {
                for(int ss=(s-1)&s; ss; ss=(ss-1)&s) {
                    int now=dp[i][ss]+dp[i][s-ss];
                    dp[i][s]=min(dp[i][s],now);
                }
                if(dp[i][s]!=INF)
                    que.push(i),in[i]=1;
            }
            spfa(s);
        }

        //求出每种联通状态的最小代价
        //dp2[mxst-1]就是全部要求联通点互相联通的代价
        mst(dp2,INF);
        for(int s=0; s<mxst; s++) {
            for(int i=1; i<=n; i++) {
                dp2[s]=min(dp2[s],dp[i][s]);
            }
        }

        for(int s=0; s<mxst; s++) {
            if(!check(s))
                continue;
            for(int ss=(s-1)&s; ss; ss=(ss-1)&s) {
                if(!check(ss))
                    continue;
                int now=dp2[ss]+dp2[s-ss];
                dp2[s]=min(dp2[s],now);
            }
        }
        if(dp2[mxst-1]==INF)
            printf("No solution\n");
        else
            printf("%d\n",dp2[mxst-1]);
    }
    return ;
}

//伪代码

建图
给每个需要联通的点分配一个状压位

初始化需要联通的点到只有自身的联通状态代价为0
枚举每种联通状态s
    对于每个点为根
        对于s的每一种划分(x&y=0,x|y=s)
            更新以当前点为根,联通状态为s的最小值
        如果当前状态代价<INF
            进入spfa队列并标记
    对当前联通状态进行spfa

枚举每种联通状态s
    对于每个点为根
        更新当前联通状态的最小代价

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值