hdu4085

解法:斯坦纳树,再对得到的生成树dp,因为有可能是森林的解更优

#include"bits/stdc++.h"
using namespace std;
const int N = 55;
const int inf = 0x3f3f3f3f;
int n,m,k;
int head[N],vis[N];
int f[N][1<<10],st[N],dp[1<<10];
queue<int> q;
struct edge{
    int nxt,v,w;
    edge(){}
    edge(int nxt, int v, int w):nxt(nxt),v(v),w(w){}
}E[2111];
int tail;
inline void init()
{
    tail = 0;
    memset(head,-1,sizeof(head));
    memset(vis,0,sizeof(vis));
    memset(st,0,sizeof(st));
}
inline void add(int u, int v, int w)
{
    E[tail] = edge(head[u],v,w); head[u] = tail++;
}

void spfa(int sta)
{
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = head[u]; ~i; i = E[i].nxt){
            int v = E[i].v, w = E[i].w;
            if(f[v][sta] > f[u][sta] + w){
                f[v][sta] = f[u][sta] + w;
                if(!vis[v]){
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
        vis[u] = 0;
    }
}

//预处理出st数组点的集合和f数组,不同的题目有所不同,返回最大的集合值
int prepare(int S)
{
    int cc = k+1;
    for(int i = 1; i <= n; i++)
        for(int j = 0; j < S; j++)
            f[i][j] = inf;
    for(int i = 1; i <= k; i++)
        st[i] = 1<<(i-1), f[i][st[i]] = 0;
    for(int i = n-k+1; i <= n; i++, cc++)
        st[i] = 1<<(cc-1), f[i][st[i]] = 0;
}

void stenier(int S)
{
    prepare(S);
    for(int sta = 0; sta < S; sta++){
        for(int i = 1; i <= n; i++){
            for(int s = sta&(sta-1); s; s = (s-1)&sta){
                f[i][sta] = min(f[i][sta],f[i][sta-s]+f[i][s]);
            }
            if(f[i][sta] != inf) q.push(i), vis[i] = 1;
        }
        spfa(sta);
    }
}
bool check(int s)
{
    int ans = 0;
    for(int i = 0; i < k; i++){
        if(s&(1<<i)) ans++;
        if(s&(1<<(i+k))) ans--;
    }
    return (ans == 0);
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        init();
        scanf("%d%d%d",&n,&m,&k);
        for(int i = 1; i <= m; i++){
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w);
            add(u,v,w); add(v,u,w);
        }
        int S = 1<<(2*k);
        stenier(S);
        for(int sta = 0; sta < S; sta++){
            dp[sta] = inf;
            for(int i = 1; i <= n; i++)
                dp[sta] = min(dp[sta],f[i][sta]);
        }
        for(int sta = 0; sta < S; sta++) if(check(sta)){
            for(int s = sta&(sta-1); s; s = (s-1)&sta) if(check(s)){
                dp[sta] = min(dp[sta],dp[s]+dp[sta-s]);
            }
        }
        if(dp[S-1] == inf) printf("No solution\n");
        else printf("%d\n",dp[S-1]);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值