3400. 【GDOI2014模拟】旅行

64 篇文章 0 订阅
10 篇文章 0 订阅

Description

从前有一位旅者,他想要游遍天下所有的景点。这一天他来到了一个神奇的王国:在这片土地上,有n个城市,从1到n进行编号。王国中有m条道路,第i条道路连接着两个城市ai,bi,由于年代久远,所有的道路都已经不能使用。如果要修复第i条道路,需要wi的时间。为了更好的旅行,旅者想要将某些道路修复,使得1号城市能够到达n号城市,2号城市能够到达n-1号城市..k号城市能够到达n-k+1号城市。为了满足他的要求,请问最少需要多少时间去修复道路。无解请输出-1。

Input

第一行:n,m,k

接下来m行:ai,bi,wi

含义如上所述。

Output

输出共一行:最少需要多少时间修复道路。如果始终无法满足旅者的要求,请输出-1。

Sample Input

5 5 2

1 3 4

3 5 2

2 3 1

3 4 4

2 4 3

Sample Output

9

Data Constraint

20%的数据满足:k <= 2, n<= 10, m <= 20

40%的数据满足:k <= 3, n<=100, m<=1000

70%的数据满足:k<=4, n<=1000, m<=1000

100%的数据满足:k<=4, n<=10000, m<=10000, n >= 2*k, wi<= 1000, 1 <= ai, bi <= n 

Hint

样例解释:

Solution

此题考查选手对stenir tree的灵活应用。

【Stenir tree问题】

在一张带权无向图中,有若干个黑色点,求一棵最小生成树,使得所有的黑色点都在该树上。

【解决方法】

我们用F[u][S]代表一棵以u节点为根的树,这棵树上的黑色节点情况用状态S表示,F[u][S]代表这棵树最小的权值和是多少。根据状态表示,我们可得转移方程:

F[u][S] = min(F[v][S’]+w[u][v]+F[u][S-S’]),其中S’|S = S。

注意到这个方程在转移时可能会出现循环,因为我们不知道应该是用F[u][S]来更新F[v][S]还是反之而行。也就是说当S相等时,我们应该用最短路的思想来进行转移(利用spfa或者dijkstra)。

假定spfa的复杂度为O(m),黑色点有p个,则整个算法的复杂度为O(3^p * m)。

【原问题】

回到我们的原问题,我们需要得到若干个连通块,使得i号点和n-i+1号点位于同一个连通块,由于所有的边权都是非负的,所有连通块一定是一棵树,也就是说我们需要得到若干棵树,使得i号点和n-i+1号点一定位于同一棵树内。

【解决办法】

在解决stenir tree的问题过程中,我们得到了F[u][S]数组,令good[S]=min(F[u][s])。则我们知道了将S状态联通所需要的最小代价。若在S状态中,     存在一个黑色点i不能对应黑色点n-i+1,则我们将这个状态删除。接下来就是要将这些状态组合起来,使得他们的权值和最小。我们可以用DP解决。

 

于是整个算法的复杂度为O(3^p * m)

鄙人觉得此题解已讲解详细,故co之为人所见

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 10010
#define I int
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
I n,m,k,x,y,z,f[N][300],bz[N],p[N];
I t[N*2],nx[N*2],l[N],s[N*2],q[N*8],ans[N];
I read(){
	I x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x;
}
void add(I x,I y,I z){
	t[++t[0]]=y;nx[t[0]]=l[x];s[t[0]]=z;l[x]=t[0];
}
int pd(I x){
	F(i,1,k){
		if(((x>>(i-1))&1)^((x>>(k+i-1))&1)) return 0;
	}
	return 1;
}
int main(){
	freopen("travel.in","r",stdin);
	freopen("travel.out","w",stdout);
	n=read();m=read();k=read();
	F(i,1,m){
		x=read();y=read();z=read();
		add(x,y,z);add(y,x,z);
	}
	memset(f,127,sizeof(f));
	memset(p,127,sizeof(p));
	F(i,1,k) f[i][1<<(i-1)]=f[n-i+1][1<<(k+i-1)]=0;
	F(i,k+1,n-k) f[i][0]=0;
	F(S,1,(1<<(k*2))-1){
		F(i,1,n){
			F(T,1,S-1){
				if((T|S)==S&&f[i][S-T]!=f[0][0]){
					for(int j=l[i];j;j=nx[j]){
						if(f[t[j]][T]==f[0][0]) continue;
						f[i][S]=min(f[i][S],f[t[j]][T]+s[j]+f[i][S-T]);	
					}
				}
			}
		}
		F(i,1,n){bz[i]=1;q[i]=i;}
		I hd=1,tl=n;
		while(hd<=tl){
			I x=q[hd++],y=f[x][S];bz[x]=0;
			if(y==f[0][0]) continue;
			for(int i=l[x];i;i=nx[i]){
				if(f[t[i]][S]>y+s[i]){
					f[t[i]][S]=y+s[i];
					if(!bz[t[i]]){
						bz[t[i]]=1;
						q[++tl]=t[i];
					}
				}
			}
		}
		F(i,1,n) p[S]=p[S]>f[i][S]?f[i][S]:p[S];
	}
	memset(ans,127,sizeof(ans));
	I inf=ans[0];ans[0]=0;
	F(S,1,(1<<(k*2))-1){
		F(T,0,S-1){
			if(ans[T]!=inf&&p[S-T]!=inf&&((T|S)==S)&&pd(S)&&pd(T)) ans[S]=min(ans[S],ans[T]+p[S-T]);
		}
	}
	if(ans[(1<<(k*2))-1]!=inf) printf("%d\n",ans[(1<<(k*2))-1]);
	else printf("-1\n");
	return 0;
}


作者:zsjzliziyang 
QQ:1634151125 
转载及修改请注明 
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/97967551

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值