DTOJ#5016. 最短路径问题

宫水三叶喜欢旅游。

三叶来到了一个 n n n 个点的城市,点与点之间有边相连,边有长度,即三叶通过所需时间。

三叶制定了一份旅游计划表。

计划表上总共包含 k k k 个计划,每个计划可以用两个节点 a i , b i a_i,b_i ai,bi 来描述。因为两个节点有一定联系,所以第 i i i 个计划是先到达节点 a i a_i ai ,然后到达节点 b i b_i bi

三叶认为计划一定要按顺序来完成,所以在还没完成第 i i i 个计划时,是不会进行第 i + 1 i+1 i+1 个计划的。

三叶通过一些获得了两个传送门。每一次三叶到达一个节点 x x x 后可以做以下操作。

  • 三叶可以不操作
  • 三叶可以选择图上任意一个传送门,并将其收回。
  • 如果 x x x 没有传送门,并且图上的传送门个数不到 2 2 2 个,那么三叶可以在 x x x 上设立传送门。
  • 如果 x x x 有传送门,并且图上还有一个传送门在 y y y ,那么三叶可以不花费任何时间从 x x x 走到 y y y 。当然,三叶也可以放弃这次机会。

以上操作均不耗时。

三叶一开始在节点 1 1 1 。那么请问,三叶完成所有计划的最小时间是多少。

第一行,三个整数 n , m , k n,m,k n,m,k ,表示点数,边数,计划数。

接下来 m m m 行,每行三个整数 u i , v i , w i u_i,v_i,w_i ui,vi,wi ,表示一条连接 u i , v i u_i,v_i ui,vi 的长度为 w i w_i wi 的双向边。

接下来 k k k 行,每行两个整数 a i , b i a_i,b_i ai,bi ,描述一个计划。

输出一行,表示最小花费时间。

样例输入 1
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
样例输出 1
5
样例输入 2
6 10 3
1 1 6
5 6 9
3 5 8
1 4 1
2 4 7
6 6 10
1 4 2
6 5 10
3 5 2
3 1 9
1 5
2 5
4 3
样例输出 2
28
样例输入 3
6 10 3
1 1 3
3 1 1
6 2 3
1 6 10
4 1 1
3 1 2
5 6 9
5 4 10
6 3 4
3 4 4
3 5
3 6
6 5
样例输出 3
16
样例数据 4

见下发文件。

本题采用捆绑测试。

对于所有数据,满足 1 ≤ n , k ≤ 300 , 1 ≤ m ≤ 4 × 1 0 4 , 0 ≤ w i ≤ 1 0 9 1\le n,k \le 300,1\le m \le 4\times 10^4,0\le w_i \le 10^9 1n,k300,1m4×104,0wi109

保证图为连通图。

子任务编号 n , k n,k n,k分值
1 1 1 ≤ 40 \le 40 40 30 30 30
2 2 2 ≤ 80 \le 80 80 30 30 30
3 3 3 ≤ 300 \le 300 300 40 40 40

大水题,直接DP但得发现只有一个传送门有用。

#include<bits/stdc++.h>
#define N 305
#define M 40004
typedef long long ll;
using namespace std;
const ll inf=1e16;
inline int read(){
	int x=0,f=1;char s=getchar();
	while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
	while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+s-'0';s=getchar();}
	return x*f;
}
ll g[N][N],f[2][N],m1;
int a[N<<1],b[N<<1];
int main(){
 //   freopen("data.in","r",stdin);
 //   freopen("path.out","w",stdout);
	int n=read(),m=read(),k=read();
	for(int i=1;i<=n;++i)for(int j=i+1;j<=n;++j)g[i][j]=g[j][i]=inf;
    for(int i=1;i<=m;++i){
    	int x=read(),y=read();ll z=read();
    	g[x][y]=min(g[x][y],z);g[y][x]=min(g[y][x],z);
    }
    a[1]=1;
    int le=1;
    for(int i=1;i<=k;++i){
    	int x=read(),y=read();
    	b[le]=x;a[++le]=x,b[le]=y;a[++le]=y;
    }
    	
    for(int z=1;z<=n;++z){
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<=n;++j){
    			g[i][j]=min(g[i][j],g[i][z]+g[z][j]);
    		}
    	}
    }
    le--;k=le;
    for(int i=1;i<=n;++i)f[0][i]=inf;
    f[0][1]=0;
    for(int i=1;i<=k;++i){
    	int now=i&1,x=a[i],y=b[i];
    	for(int j=1;j<=n;++j)f[now][j]=inf;m1=inf;
    	for(int j=1;j<=n;++j)m1=min(m1,f[now^1][j]);
    	for(int j=1;j<=n;++j){
    		f[now][j]=min(f[now][j],g[x][y]+f[now^1][j]);
    	}
    	for(int j=1;j<=n;++j){
    		f[now][j]=min(f[now][j],g[x][j]+g[j][y]+m1);
    	}
    	for(int j=1;j<=n;++j){
    		f[now][j]=min(f[now][j],f[now^1][j]+g[j][y]);
    	}
    	for(int j=1;j<=n;++j)for(int h=1;h<=n;++h){
    		f[now][j]=min(f[now][j],g[x][j]+g[h][y]+f[now^1][h]);
    	}
    	for(int j=1;j<=n;++j)for(int h=1;h<=n;++h){
    		f[now][j]=min(f[now][j],g[h][j]+g[j][y]+f[now^1][h]);
    	}
    }
    ll ans=inf;
    for(int j=1;j<=n;++j)ans=min(ans,f[k&1][j]);
    printf("%lld\n",ans);
    	
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值