ZOJ 3613 Wormhole Transport

斯坦纳树,$dp$。

先求出每个状态下连通的最小花费,因为可以是森林,所以$dp$一下。

#include<bits/stdc++.h>
using namespace std;

int n;
const int INF = 0x7FFFFFFF;
struct X
{
    int p,s,q;
}e[210];
int id[210];
vector<int>g[210];
int val[210][210],d[210][1200],dp[1200];
int f[210*10000],k;
queue<int>Q;

int num[1200],cost[1200];

void spfa()
{
    while(!Q.empty())
    {
        int h = Q.front(); Q.pop(); f[h]=0;
        int x=h/10000,y=h%10000;
        for(int i=0;i<g[x].size();i++)
        {
            int to = g[x][i];

            if(to<k)
            {
                if(((1<<to)&y)==0)
                {
                    if(d[x][y]+val[x][to]<d[to][y|(1<<to)])
                        d[to][y|(1<<to)]=d[x][y]+val[x][to];
                }
            }

            else
            {
                if(d[x][y]+val[x][to]<d[to][y])
                {
                    d[to][y] = d[x][y]+val[x][to];
                    if(f[to*10000+y]==0)
                    {
                        f[to*10000+y]=1;
                        Q.push(to*10000+y);
                    }
                }
            }
        }
    }
}

bool cmp(X a,X b)
{
    if(a.p!=b.p) return a.p>b.p;
    return a.s>b.s;
}

void work(int x)
{
    int A=0,B=0;
    for(int i=0;i<k;i++)
    {
        if(((1<<i)&x)==0) continue;
        A=A+e[i].p; B=B+e[i].s;
    }
    num[x] = min(A,B);
    cost[x] = dp[x];
}

int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=0;i<n;i++)
        {
            scanf("%d%d",&e[i].p,&e[i].s);
            e[i].p = min(4,e[i].p);
            e[i].q = i;
        }
        sort(e,e+n,cmp);
        for(int i=0;i<n;i++) id[e[i].q]=i;

        for(int i=0;i<n;i++)
            for(int j=0;j<n;j++) val[i][j]=INF;
        memset(f,0,sizeof f);
        for(int i=0;i<n;i++) g[i].clear();

        int m; scanf("%d",&m);
        for(int i=1;i<=m;i++)
        {
            int a,b,c; scanf("%d%d%d",&a,&b,&c);
            a--; b--; a=id[a]; b=id[b];
            val[a][b]=min(val[a][b],c);
            val[b][a]=val[a][b];
            g[a].push_back(b);
            g[b].push_back(a);
        }

        k=0; for(int i=0;i<n;i++) if(e[i].p||e[i].s) k++;

        int st = 1<<k;
        for(int j=0;j<st;j++)
            for(int i=0;i<n;i++) d[i][j]=INF;

        for(int i=0;i<n;i++)
        {
            if(i<k) d[i][1<<i]=0;
            else d[i][0]=0;
        }

        for(int j=0;j<st;j++)
        {
            for(int i=0;i<n;i++)
            {
                if(i<k)
                {
                    if(((1<<i)&j)==0) continue;
                    for (int x = j; x; x = (x-1)&j)
                    {
                        int A=x ,B=j-A;
                        if(d[i][A|(1<<i)]!=INF&&d[i][B|(1<<i)]!=INF)
                            d[i][j] = min(d[i][j], d[i][A|(1<<i)]+d[i][B|(1<<i)]);
                    }
                }
                else
                {
                    for (int x = j; x; x = (x-1)&j)
                    {
                        int A=x ,B=j-A;
                        if(d[i][A]!=INF&&d[i][B]!=INF)
                            d[i][j] = min(d[i][j], d[i][A]+d[i][B]);
                    }
                }
                if(d[i][j]!=INF) Q.push(i*10000+j);
            }
            spfa();
        }

        for(int j=0;j<st;j++)
        {
            dp[j]=INF;
            for(int i=0;i<n;i++) dp[j]=min(dp[j],d[i][j]);
        }

        memset(num,0,sizeof num);
        memset(cost,0,sizeof cost);

        for(int i=0;i<st;i++)
        {
            if(dp[i]!=INF) work(i);
            for (int x = i; x; x = (x-1)&i)
            {
                int A=x, B=i-x;
                if(num[A]+num[B]>num[i])
                {
                    num[i] = num[A]+num[B];
                    cost[i] = cost[A]+cost[B];
                }
                else if(num[A]+num[B]==num[i])
                {
                    if(cost[A]+cost[B]<cost[i])
                        cost[i] = cost[A]+cost[B];
                }
            }
        }

        int ans1=0,ans2=999999999;
        for(int i=0;i<st;i++) ans1=max(ans1,num[i]);
        for(int i=0;i<st;i++)
        {
            if(num[i]!=ans1) continue;
            ans2=min(ans2,cost[i]);
        }
        printf("%d %d\n",ans1,ans2);

    }
    return 0;
}

 

posted @ 2017-03-29 13:02  Fighting_Heart  阅读(225)  评论(0编辑  收藏  举报