BZOJ4006 [JLOI2015]管道连接

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

 

 

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

 

Description

小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰。

该部门有 n 个情报站,用 1 到 n 的整数编号。给出 m 对情报站 ui;vi 和费用 wi,表示情
报站 ui 和 vi 之间可以花费 wi 单位资源建立通道。
如果一个情报站经过若干个建立好的通道可以到达另外一个情报站,那么这两个情报站就
建立了通道连接。形式化地,若 ui 和 vi 建立了通道,那么它们建立了通道连接;若 ui 和 vi 均
与 ti 建立了通道连接,那么 ui 和 vi 也建立了通道连接。
现在在所有的情报站中,有 p 个重要情报站,其中每个情报站有一个特定的频道。小铭铭
面临的问题是,需要花费最少的资源,使得任意相同频道的情报站之间都建立通道连接。

Input

第一行包含三个整数 n;m;p,表示情报站的数量,可以建立的通道数量和重要情报站的数

量。接下来 m 行,每行包含三个整数 ui;vi;wi,表示可以建立的通道。最后有 p 行,每行包含
两个整数 ci;di,表示重要情报站的频道和情报站的编号。

Output

输出一行一个整数,表示任意相同频道的情报站之间都建立通道连接所花费的最少资源总量。

Sample Input

5 8 4
1 2 3
1 3 2
1 5 1
2 4 2
2 5 1
3 4 3
3 5 1
4 5 1
1 1
1 2
2 3
2 4

Sample Output

4

HINT

 

选择 (1; 5); (3; 5); (2; 5); (4; 5) 这 4 对情报站连接。


对于 100% 的数据,0 <ci <= p <= 10; 0 <ui;vi;di <= n <= 1000; 0 <= m <= 3000; 0 <= wi <=

20000。
 
 
 
正解:斯坦纳树
解题报告:
  听说这道题跟我写的上一道题很像…
  这道题略有不同的是,不同频道之间的情报站的合并需要考虑清楚哪些是合法状态,哪些是非法的。
  也就是说,不能只处理单个的部分,因为有可能会漏掉一些解!
  最好的处理方法就是在最后综合答案的时候再把状态的合法性判断进去。

 

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
using namespace std;
typedef long long LL;
const int MAXN = 3011;
const int MAXM = 8011;
const int MAXS = 2024;
int n,m,k,ecnt,first[MAXN],to[MAXM],w[MAXM],next[MAXM],id[12][MAXN],f[MAXN][MAXS],S,head,tail,dui[MAXN*100],g[MAXS],cnt,zhuang[MAXN],match[MAXN];
bool vis[MAXN];
inline void link(int x,int y,int z){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
    if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}

inline bool OK(int s){
	for(int i=1;i<=10;i++) { 
		if(zhuang[i]==0) continue;
		if(( ( zhuang[i]&s )!=0) && ( (s&zhuang[i])!=zhuang[i] ))
			return false;  
	}
	return true; 
}

inline void SPFA(int s){
	head=tail=0; int u; for(int i=1;i<=n;i++) if(f[i][s]!=f[0][0]) dui[++tail]=i,vis[i]=1;
	while(head<tail) {
		head++; u=dui[head]; vis[u]=0;
		for(int i=first[u];i;i=next[i]) {
			int v=to[i]; 
			if(f[v][s]>f[u][s]+w[i]) {
				f[v][s]=f[u][s]+w[i];
				if(!vis[v]) { vis[v]=1; dui[++tail]=v; }
			}
		}
	}
}

inline void work(){
	n=getint(); m=getint(); k=getint(); int x,y,z,now;
	for(int i=1;i<=m;i++) {	x=getint(); y=getint(); z=getint();	link(x,y,z); link(y,x,z); }

	memset(f,0x3f,sizeof(f)); S=(1<<k)-1;
	for(int i=1;i<=k;i++) {
		x=getint(); y=getint();
		if(id[x][0]==0) cnt++;
		id[x][++id[x][0]]=y;
		f[y][(1<<(i-1))]=0;
		match[y]=(1<<(i-1));
	}
	for(int s=1;s<=S;s++) {
		for(int i=1;i<=n;i++) 
			for(int from=(s-1)&s;from;from=(from-1)&s) {
				if(f[i][from]==f[0][0] || f[i][s-from]==f[0][0]) continue;
				now=f[i][from]+f[i][s-from];
				if(f[i][s]>now) f[i][s]=now;
			}
		SPFA(s);
	}

	for(int i=1;i<=cnt;i++) 
		for(int j=1;j<=id[i][0];j++)
			zhuang[i]|=match[id[i][j]];

	memset(g,0x3f,sizeof(g));
	//不能只处理单个的部分!因为有可能会漏掉一些解!最好的处理方法就是在最后综合答案的时候再把状态的合法性判断进去
	for(int s=1;s<=S;s++) for(int i=1;i<=n;i++) if(f[i][s]<g[s]) g[s]=f[i][s];
	/*
	for(int i=1;i<=10;i++) {
		if(zhuang[i]==0) continue;
		for(int j=1;j<=n;j++) g[zhuang[i]]=min(f[j][zhuang[i]],g[zhuang[i]]);
	}*/
	for(int s=1;s<=S;s++) {
		if(!OK(s)) continue;//判断合法性
		for(int i=(s-1)&s;i;i=(i-1)&s) {
			if(g[i]==g[0] || g[s-i]==g[0]) continue;
			if(!OK(i)) continue;
			g[s]=min(g[s],g[i]+g[s-i]);
		}
	}
	printf("%d",g[S]);
}

int main()
{
    work();
    return 0;
}

  

posted @ 2017-02-20 14:26  ljh_2000  阅读(383)  评论(3编辑  收藏