HDU 4085 斯坦纳树模板题

Dig The Wells

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)

Total Submission(s): 971    Accepted Submission(s): 416

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

题意:有n个和尚,每一个和尚一个庙,有m个村庄,p条路,每条路有费用,每一个地方打井也需要费用,求最少花多少钱可以使得所有和尚喝上水。

斯坦纳树比较裸的问题。

代码:

/* ***********************************************
Author :rabbit
Created Time :2014/7/17 0:59:57
File Name :13.cpp
************************************************ */
#pragma comment(linker, "/STACK:102400000,102400000")
#include <stdio.h>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <string>
#include <time.h>
#include <math.h>
#include <queue>
#include <stack>
#include <set>
#include <map>
using namespace std;
#define INF 100000000
#define eps 1e-8
#define pi acosi
typedef long long ll;
int head[1100],tol;
struct Edge{
	int next,to,val;
}edge[1001000];
void addedge(int u,int v,int w){
	edge[tol].to=v;
	edge[tol].next=head[u];
	edge[tol].val=w;
	head[u]=tol++;
}
int weight[1100],d[1100][1<<5],dp[1100],in[1010][1<<5];
int main()
{
     //freopen("data.in","r",stdin);
     //freopen("data.out","w",stdout);
     int n,m,p;
	 while(~scanf("%d%d%d",&n,&m,&p)){
		 memset(head,-1,sizeof(head));tol=0;
		 for(int i=0;i<n+m;i++)
			 scanf("%d",&weight[i]);
		 while(p--){
			 int u,v,w;
			 scanf("%d%d%d",&u,&v,&w);
			 u--;v--;
			 addedge(u,v,w);
			 addedge(v,u,w);
		 }
		 for(int i=0;i<n+m;i++)
			 for(int j=0;j<(1<<n);j++)
				 d[i][j]=INF;
		 for(int i=0;i<(1<<n);i++)dp[i]=INF;
		 memset(in,0,sizeof(in));
		 for(int i=0;i<n;i++)
			 d[i][1<<i]=weight[i];
		 for(int i=1;i<(1<<n);i++){
			 queue<int> Q;
			 for(int j=0;j<n+m;j++){
				 for(int k=i&(i-1);k;k=(k-1)&i)
					 d[j][i]=min(d[j][i],d[j][i-k]+d[j][k]-weight[j]);
				 if(d[j][i]<INF)Q.push(100000*j+i),in[j][i]=1;
			 }
			 while(!Q.empty()){
				 int v=Q.front()/100000,t=Q.front()%100000;
				 Q.pop();
				 in[v][t]=0;
				 for(int e=head[v];e!=-1;e=edge[e].next){
					 int s=edge[e].to;
					 if(d[s][t]>d[v][t]+edge[e].val+weight[s]-weight[v]){
						 d[s][t]=d[v][t]+edge[e].val+weight[s]-weight[v];
						 if(!in[s][t]){
							 in[s][t]=1;
							 Q.push(100000*s+t);
						 }
					 }
				 }
			 }
		 }
		 for(int i=1;i<(1<<n);i++)
			 for(int j=0;j<n+m;j++)
				 dp[i]=min(dp[i],d[j][i]);
		 for(int i=1;i<(1<<n);i++){
			 for(int j=i&(i-1);j;j=i&(j-1))
				 dp[i]=min(dp[i],dp[j]+dp[i-j]);
		 }
		 cout<<dp[(1<<n)-1]<<endl;
	 }
     return 0;
}

HDU 4085 斯坦纳树模板题,布布扣,bubuko.com

时间: 2024-08-05 11:18:41

HDU 4085 斯坦纳树模板题的相关文章

HDU 4085 斯坦纳树

题目大意: 给定无向图,让前k个点都能到达后k个点(保护地)中的一个,而且前k个点每个需要占据后k个中的一个,相互不冲突 找到实现这个条件达到的选择边的最小总权值 这里很容易看出,最后选到的边不保证整个图是联通的 我们只要计算出每一个连通的最小情况,最后跑一遍dfs就能计算出答案了 那么用dp[i][j]表示 i 点为根得到联通状态为 j 的情况需要选到的边的最小总权值 这个用斯坦纳树的思想就可以做到的 对于每一个状态,都用spfa跑一遍得到最优解 dp[i][j] = min(dp[i][j]

HDU 4085 斯坦纳树+DP

https://cn.vjudge.net/problem/HDU-4085 给你n,m,k ,分别表示有n个点,m条边,每条边有一个权值,表示修复这条边需要的代价 从前k个点中任取一个使其和后k个点中的某一个点,通过边连接,并且必须是一一对应,问最小的代价是多少. 原文地址:https://www.cnblogs.com/Aragaki/p/10989578.html

斯坦纳树模板

屌炸天阿什么东西都有 丢 //斯坦纳树模板 让k个点联通的最小生成树 复杂度 n*3^k #include<iostream> #include<cstring> #include<set> #include<map> #include<cmath> #include<stack> #include<queue> #include<deque> #include<list> #include<

【模板】斯坦纳树

链接: #include <stdio.h> int main() { puts("转载请注明出处[辗转山河弋流歌 by 空灰冰魂]谢谢"); puts("网址:blog.csdn.net/vmurder/article/details/46499973"); } 题目: 斯坦纳树 Time Limit: 1 Sec Memory Limit: 128 MB Description 现在有一个n*m的矩阵,某些元素为0,剩下的元素大于0. 现在你要选择一

2019ICPC南昌邀请赛现场赛A题 - Attack(斯坦纳树)

题意: 给出一张图,求让\(4\)对点相互可以到达的最小边权值.仅要求一对之间,一对与另外一对可到达也可不到达. 分析: 斯坦纳树裸题,众所周知斯坦纳树仅能求出这\(4\)对点(关键点)的连通状况,如这\(4\)对点相互都连通,某点和某点连通等.然而让这\(4\)对点连通符合题目要求,但不一定是最优解(我可以让每对点直接相连),所以我们要对斯坦纳树求出的\(dp\)数组进行子集\(dp\)才能得到最优解. #include <bits/stdc++.h> using namespace std

【bzoj5180】[Baltic2016]Cities 斯坦纳树

题目描述 给定n个点,m条双向边的图.其中有k个点是重要的.每条边都有一定的长度. 现在要你选定一些边来构成一个图,要使得k个重要的点相互连通,求边的长度和的最小值. 输入 共m+2行 第1行:n,k,m,n个点,k个重要的点,m条边: 第2行共K个点 第3至第m+2行,每行包括3个数字,a,b,c,表示有一条从a到b长度为c的双向路径 k<=5 n<=10^5 1<=m<=2*(10^5) 输出 共1行,即最小长度和 样例输入 4 3 61 3 41 2 41 3 91 4 62

BZOJ_5180_[Baltic2016]Cities_ 斯坦纳树

题意: 给定n个点,m条双向边的图.其中有k个点是重要的.每条边都有一定的长度. 现在要你选定一些边来构成一个图,要使得k个重要的点相互连通,求边的长度和的最小值. 分析: 斯坦纳树裸题 dis[i][j]表示关键点连通状态为i,当前在点j的最小花费 有两个转移:内部枚举子集,外部spfa转移 这道题卡spfa,那我们用dij就好啦 代码: #include <stdio.h> #include <string.h> #include <algorithm> #incl

BZOJ_2595_[Wc2008]游览计划_斯坦纳树

题意: 分析: 斯坦纳树裸题,有几个需要注意的地方 给出矩阵,不用自己建图,但枚举子集转移时会算两遍,需要减去当前点的权值 方案记录比较麻烦,两边的转移都需要记录,最后dfs找方案会比较容易理解 代码: #include <stdio.h> #include <string.h> #include <algorithm> #include <queue> using namespace std; #define N 110 #define LL long l

HDU 4085 Peach Blossom Spring 记忆化搜索枚举子集 斯坦纳树

题目链接:点击打开链接 题意: 第一行输入n个点 m条可修建的无向边 k个人 下面给出修建的边和修建该边的花费. 开始时k个人在1-k的每个点上(一个点各一人) 目标:从m条给定边中修建部分边使得花费和最小 让k个人移动到 [n-k+1, n] 后面的k个点上(每个点放一个人). 思路: 首先就是一道斯坦纳树,还是先求一个dp数组(求解方法:点击打开链接) dp[i][j] 表示以i为根 ,j为8个点中是否在 i 的子树里 时的最小花费. 现在的问题就是如何求答案. 因为一个人到他的目标点这条路