HDU 4085 Peach Blossom Spring(斯坦纳树+dp)

题意:

给你一些点和边,让你选择一下边,是的前k个点和后k个点连通且代价最小,但是除了这2k个点,好友一些其他的点。
一开始我以为是MST,后来发现 不行,然后就是网络流乱搞,然并卵,就是因为有其他的点可用可不用。
后来一看才知道原来是斯坦纳树。。。。如此高级的东西。
斯坦纳树的定义:斯坦纳树问题是组合优化问题,与最小生成树相似,是最短网络的一种。
最小生成树是在给定的点集和边中寻求最短网络使所有点连通。而最小斯坦纳树允许在给定点外增加额外的点,使生成的最短网络开销最小。
借这个主要是考状态压缩,毕竟np问题。。。
给个好的学习链接:戳这里
因为最后有可能会生成一个森林,所以要用dp来合并答案。

代码:

//
//  Created by  CQU_CST_WuErli
//  Copyright (c) 2016 CQU_CST_WuErli. All rights reserved.
//
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <string>
#include <vector>
#include <list>
#include <map>
#include <queue>
#include <stack>
#include <set>
#include <algorithm>
#include <sstream>
#define CLR(x) memset(x,0,sizeof(x))
#define OFF(x) memset(x,-1,sizeof(x))
#define MEM(x,a) memset((x),(a),sizeof(x))
#define BUG cout << "I am here" << endl
#define lookln(x) cout << #x << "=" << x << endl
#define SI(a) scanf("%d",&a)
#define SII(a,b) scanf("%d%d",&a,&b)
#define SIII(a,b,c) scanf("%d%d%d",&a,&b,&c)
#define rep(flag,start,end) for(int flag=start;flag<=end;flag++)
#define Rep(flag,start,end) for(int flag=start;flag>=end;flag--)
#define Lson l,mid,rt<<1
#define Rson mid+1,r,rt<<1|1
#define Root 1,n,1
const int INF_INT=0x3f3f3f3f;
const long long INF_LL=0x7fffffff;
const int MOD=1e9+7;
const double eps=1e-9;
const double pi=acos(-1);
typedef long long  ll;
using namespace std;

const int N=6e3;
const int M=100;
int t;
int n,m,k;
int pnt[N],head[M],nxt[N],w[N],cnt;
int s[M];
int vis[M][1<<12];
int d[M][1<<12],dp[1<<12];

struct Sta
{
    int id,state;
    Sta(int id,int state):id(id),state(state){}
};
queue<Sta> q;

void add_edge(int u,int v,int c) {
    pnt[cnt]=v;nxt[cnt]=head[u];w[cnt]=c;head[u]=cnt++;
}

void read() {
    SIII(n,m,k);
    CLR(s);CLR(vis);
    OFF(head);cnt=0;
    rep(i,1,m) {
        int u,v,c;
        SIII(u,v,c);
        // cout << u << ' ' << v << ' ' << c << endl;
        add_edge(u,v,c);
        add_edge(v,u,c);
    }
    MEM(d,INF_INT);
    rep(i,1,k) {
        s[i]=1<<(i-1),d[i][s[i]]=0;
        s[n-i+1]=1<<(k+i-1),d[n-i+1][s[n-i+1]]=0;
    }
}

bool check(int sta) {
    int ret=0;
    for(int i=0;sta;i++,sta>>=1)
        ret+=(sta&1)*(i<k?1:-1);
    return ret==0;
}

void spfa() {
    while(q.size()) {
        Sta now=q.front();q.pop();
        int id=now.id,sta=now.state;
        vis[id][sta]=0;
        for (int i=head[id];~i;i=nxt[i]) {
            int v=pnt[i];
            if (d[v][sta|s[v]]>d[id][sta]+w[i]) {
                d[v][sta|s[v]]=d[id][sta]+w[i];
                if (sta==(sta|s[v]) && !vis[v][sta]) {
                    vis[v][sta]=1;
                    q.push(Sta(v,sta));
                }
            }
        }
    }
}

void solve() {
    for (int S=0;S<1<<(2*k);S++) {
        for (int root=1;root<=n;root++) {
            for (int i=(S-1)&S;i;i=(i-1)&S)
                d[root][S]=min(d[root][S],d[root][i|s[root]]+d[root][(S-i)|s[root]]);
            if (d[root][S]<INF_INT) q.push(Sta(root,S)),vis[root][S]=1;
        }
        spfa();
    }
    for (int S=0;S<1<<(2*k);S++) {
        dp[S]=INF_INT;
        rep(i,1,n) dp[S]=min(dp[S],d[i][S]);
    }
    for (int S=1;S<1<<(2*k);S++) {
        if (check(S)) {
            for (int i=(S-1)&S;i;i=(i-1)&S) {
                if (check(i)) {
                    dp[S]=min(dp[S],dp[S-i]+dp[i]);
                }
            }
        }
    }
    if (dp[(1<<(2*k))-1]>=INF_INT) puts("No solution");
    else cout << dp[(1<<(2*k))-1] << endl;
}

int main(int argc, char const *argv[]) {
#ifdef LOCAL
    freopen("C:\\Users\\john\\Desktop\\in.txt","r",stdin);
    // freopen("C:\\Users\\john\\Desktop\\out.txt","w",stdout);
#endif
    SI(t);
    while(t--) {
        read();
        solve();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值