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