hdu3311 - Dig The Wells (斯坦纳树 spfa + DP)

Dig The Wells

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 1037    Accepted Submission(s): 453


Problem Description
You may all know the famous story “Three monks”. Recently they find some places around their temples can been used to dig some wells. It will help them save a lot of time. But to dig the well or build the road to transport the water will cost money. They do not want to cost too much money. Now they want you to find a cheapest plan.
 

Input
There are several test cases.
Each test case will starts with three numbers n , m, and p in one line, n stands for the number of monks and m stands for the number of places that can been used, p stands for the number of roads between these places. The places the monks stay is signed from 1 to n then the other m places are signed as n + 1 to n + m. (1 <= n <= 5, 0 <= m <= 1000, 0 <=p <= 5000)
Then n + m numbers followed which stands for the value of digging a well in the ith place.
Then p lines followed. Each line will contains three numbers a, b, and c. means build a road between a and b will cost c.
 

Output
For each case, output the minimum result you can get in one line.
 

Sample Input
  
  
3 1 3 1 2 3 4 1 4 2 2 4 2 3 4 4 4 1 4 5 5 5 5 1 1 5 1 2 5 1 3 5 1 4 5 1
 

Sample Output
  
  
6 5
 

Author
dandelion
 

Source
HDOJ Monthly Contest – 2010.02.06


题意:n个寺庙,m个空地,寺庙与空地之间p条路,在寺庙和空地选择地点,挖若干口井使寺庙都能有水且造价最少 (打井,修路要费用)

解析:因每个寺庙只需找到一口井,也就是寺庙的位置到井的位置只需一条路,所以我们的最后的最优解一定得到的是一棵树,可以增加一个0点来连接所有的点,其边权就是挖井的花费,所以最终以0点做为树的根节点,这样只要是到达0点则说明井己挖好。因任意两个寺庙只有两种情况(到同一口井或不同的井),到同一口井时可能最近公共父节点到井相隔几个点。不同井则最近公共父节点一定是0点。
DP[寺庙的组成状态][子树的根节点]:最小花费。
dp[ j ] [ i ] =min{ dp[ j ] [ i ] ,dp[ k ] [ i ] +dp[ l ] [ i ]  },其中k和l是对j的一个划分。
dp[ j ] [ i ] =min{ dp[ j ] [ i ] ,dp[ j ] [ i' ] +w[ i' ] [ i ] },其中i和i'之间有边相连。

#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
typedef  long long LL ;

const LL mod=1000000009LL;
const int N=10000;
const int inf=0x3fffffff;

int T;
int n,m,k;

struct edge
{
    int v,c;
    edge(int vi,int ci)
    {
        v=vi,c=ci;
    }
};
vector<edge> mp[1200];

int dis[1200][1200];

int dp[1<<7][1010];


void spfa()
{
    int inq[N]= {0};

    queue<int> q;
    for(int i=0; i<=n+m; i++)
    {
        for(int t=0; t<=n+m; t++)
            dis[i][t]=inf;
        dis[i][i]=0;

        q.push(i);

        while(!q.empty())
        {
            int s=q.front();
            q.pop();
            inq[s]=0;
            int sz=mp[s].size();

            for(int j=0; j<sz; j++)
            {
                int v=mp[s][j].v;
                //int c=mp[s][j].c;

                if(dis[i][v] > dis[i][s] + mp[s][j].c)
                {
                    dis[i][v] = dis[i][s] + mp[s][j].c;
                    if(!inq[v])
                    {
                        q.push(v);
                        inq[v]=1;
                    }
                }
            }
        }
    }
}


void DP()
{

    for(int sta=1; sta<(1<<(n+1)); sta++)
        for(int i=0; i<=n+m; i++)
            dp[sta][i]=inf;

    for(int i=0; i<=n; i++)
        for(int j=0; j<=n+m; j++)
            dp[1<<i][j] = dis[i][j];

    for(int sta=1; sta<(1<<(n+1)); sta++)
    {
        if(sta&(sta-1))
        {
            for(int i=0; i<=n+m; i++)
                for(int s=sta; s>0; s=(s-1)&sta)
                {
                    if(dp[sta][i] > dp[sta^s][i] + dp[s][i])
                    {
                        dp[sta][i] = dp[sta^s][i] + dp[s][i];
                    }
                }

            for(int i=0;i<=n+m;i++)
              for(int j=0;j<=n+m;j++)
              {
                  if(dp[sta][i] > dp[sta][j] + dis[j][i])
                    dp[sta][i]=dp[sta][j] + dis[j][i];
              }
        }
    }
}

int a,b,c;

int main()
{
    while(scanf("%d%d%d",&n,&m,&k)!=EOF)
    {
        for(int i=0;i<=n+m;i++) mp[i].clear();

        for(int i=1; i<=n+m; i++)
        {
            scanf("%d",&a);

            mp[0].push_back(edge(i,a));
            mp[i].push_back(edge(0,a));
        }

        for(int i=0; i<k; i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            mp[a].push_back(edge(b,c));
            mp[b].push_back(edge(a,c));
        }

        spfa();

        DP();
        printf("%d\n",dp[(1<<(n+1))-1][0]);

    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值