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
6 5
【题目大意】
给定N个寺庙,和M个另外的地方。
然后给定点权,表示在这个点挖水井需要的代价。
再给定边权,为建造无向边i,j的代价。
然后求怎样弄最小的代价使得前N个点,就是寺庙都能从挖的井里得到水。
解析:因每个寺庙只需找到一口井,也就是寺庙的位置到井的位置只需一条路,所以我们的最后的最优解一定得到的是一棵树,可以增加一个0点来连接所有的点,其边权就是挖井的花费,所以最终以0点做为树的根节点,这样只要是到达0点则说明井己挖好。因任意两个寺庙只有两种情况(到同一口井或不同的井),到同一口井时可能最近公共父节点到井相隔几个点。不同井则最近公共父节点一定是0点。#include<stdio.h>
#include<vector>
#include<queue>
using namespace std;
#define move(a) (1<<(a))
const int N = 1010;
const int inf = 0x3fffffff;
struct EDG
{
int v,c;
};
int n,m,dp[1<<7][N],dis[N][N];
vector<EDG>map[N];
void spfa()//求两点之间的最短距离
{
int inq[N]={0},ts,k;
queue<int>q;
for(int s=0;s<=n+m;s++)
{
for(int j=0;j<=n+m;j++)
dis[s][j]=inf;
dis[s][s]=0;
q.push(s);
while(!q.empty())
{
ts=q.front(); q.pop();
inq[ts]=0;
k=map[ts].size();
for(int j=0;j<k;j++)
{
int v=map[ts][j].v;
if(dis[s][v]>dis[s][ts]+map[ts][j].c)
{
dis[s][v]=dis[s][ts]+map[ts][j].c;
if(!inq[v])
q.push(v),inq[v]=1;
}
}
}
}
}
void countDp()
{
for(int sta=1;sta<move(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[move(i)][j]=dis[i][j];
for(int sta=1;sta<move(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 main()
{
int p,a,b;
EDG e;
while(scanf("%d%d%d",&n,&m,&p)>0)
{
for(int i=0;i<=n+m;i++)
map[i].clear();
for(int i=1;i<=n+m;i++)
{
scanf("%d",&e.c);
e.v=i; map[0].push_back(e);//用挖井的花费作为i-->0边的花费,所以最终1~n点都要归于一点0
e.v=0; map[i].push_back(e);
}
while(p--)
{
scanf("%d%d%d",&a,&b,&e.c);
e.v=b; map[a].push_back(e);
e.v=a; map[b].push_back(e);
}
spfa();
countDp();
printf("%d\n",dp[move(n+1)-1][0]);
}
}
HDU3311Dig The Wells(斯坦纳树,spfa+状态压缩DP)可作模板
原文:http://blog.csdn.net/u010372095/article/details/44656931