HDU 4085 斯坦纳树

近期更新可能会比较少,只会更新新的知识点,因为要尽快多刷一些题。。。。
详解请参照:详解
然后按照思路,自己搞的份模版。

/* 
 *  Steiner Tree:求,使得指定K个点连通的生成树的最小总权值 
 *  st[i] 表示顶点i的标记值,如果i是指定集合内第m(0<=m<K)个点,则st[i]=1<<m  
 *  endSt=1<<K 
 *  dptree[i][state] 表示以i为根,连通状态为state的生成树值 
 */  
#include <bits/stdc++.h>
using namespace std;

const int MAXN  = 50+7;
const int inf = 1e8;
typedef long long LL;
struct node
{
    int v,w;
};
vector<node>head[MAXN];
int n,m,k;
int R;
int dp[MAXN][1<<10];
int d[1<<10];
int s[MAXN];

void init()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= n; ++i)
    {
        head[i].clear();
        s[i] = 0;
    }
    R = 1<<(k<<1);
    for(int i = 1; i <= n; ++i)
        for(int j = 0; j < R; ++j)dp[i][j] = inf;
    int u,v,w;
    while(m--)
    {
        scanf("%d%d%d",&u,&v,&w);
        head[u].push_back(node{v,w});
        head[v].push_back(node{u,w});
    }
    for(int i = 1; i <= k; ++i)
    {
        s[i] = 1<<(i-1),dp[i][s[i]] = 0;
        s[n-i+1] = 1<<(k+i-1),dp[n-i+1][s[n-i+1]] = 0;
    }
}

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

bool updata(int x,int y,int w)
{
    if(w < dp[x][y])
    {
        dp[x][y] = w;
        return 1;
    }
    return 0;
}
queue<int>q;
bool vis[MAXN][1<<10];
void spfa(int state)
{
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u][state] = 0;
        for(node x : head[u])
        {
            int v = x.v;
            int w = x.w;
            if(updata(v,s[v]|state,dp[u][state] + w) && state == (state|s[v]) && !vis[v][state])
            {
                vis[v][state] = 1;
                q.push(v);
            }
        }
    }
}

void steinerTree()
{
    for(int state = 0; state < R; ++state)
    {
        for(int i = 1; i <= n; ++i)
        {
            for(int j = (state-1)&state; j; j = (j-1)&state)
            {
                //printf("%d %d %d %d\n",i,j|s[i],i,(state-j)|s[i]);
                dp[i][state] = min(dp[i][state],dp[i][j|s[i]] + dp[i][(state-j)|s[i]]);
            }
            if(dp[i][state] < inf)q.push(i),vis[i][state] = 1;
        }
        spfa(state);
    }
}


int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        steinerTree();
        for(int state = 0; state < R; ++state)
        {
            d[state] = inf;
            for(int i = 1; i <= n; ++i)d[state] = min(d[state],dp[i][state]);
        }
        for(int state = 1; state < R; ++state)
        {
            if(check(state))
            {
                for(int sub = (state-1)&state; sub; sub = (sub-1)&state)
                {
                    if(check(sub))d[state] = min(d[state],d[sub] + d[state - sub]);
                }
            }
        }
        if(d[R-1] >= inf)puts("No solution");
        else printf("%d\n",d[R-1]);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值