2020牛客暑期多校训练营(第五场) A Portal

题目描述
You are now in a big factory. The factory could be recognized as a graph with n vertices and m edges. Every edge has its length. You have k missions to do. The i-th mission is going to vertex a_ia
i

, picking a block and then sending it to vertex b_ib
i

. You should complete the missions in the order from 1-st to k-th. Initially you are standing at vectex 1.

You have a gun in your hand. When you are at some vertex u, you could shoot the gun at the ground, and then a portal will be built at vertex u. When there are two portals in the factory, assuming they are at u and v, you could transfer between u and v with no cost(just like an edge connecting u and v with length 0).

You also have a remote controller in your hand. It allows you to close a portal whenever you want and wherever you are(closing one portal at a time, not all portals at the same time). What’s more, there could be at most two existing portals. So if you want to create another portal when there exists two, you must use your controller to close one before you create.

You need to find the minimum distance you have to walk in order to complete all k missions.
输入描述:
First line contains three positive integers n, m, k separated by spaces.

Next m lines, each contains three integers u_iu
i

, v_iv
i

, w_iw
i

, indicating a bidirectional edge connecting vertex u_iu
i

and v_iv
i

with length w_iw
i

.

Next k lines, each contains two intergers a_i, b_ia
i

,b
i

, indicating the begin and the end of the i-th mission.

1 \leq n \leq 3001≤n≤300

1 \leq m \leq 400001≤m≤40000

1 \leq k \leq 3001≤k≤300

1 \leq u_i, v_i \leq n1≤u
i

,v
i

≤n

0 \leq w_i \leq 10^90≤w
i

≤10
9

1 \leq a_j, b_j \leq n1≤a
j

,b
j

≤n

The graph is guaranteed to be connected.
输出描述:
Output one integer indicating the minimum distance.
示例1
输入
复制
5 4 2
1 2 1
2 3 1
3 4 1
4 5 1
1 5
2 4
输出
复制
5
说明
Solution for sample 1: walk from 1 to 5, create portals at 2 and 4 when passing by. And then walk from 5 to 4, then you could use portals to complete the second mission.
示例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
输出
复制
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
输出
复制
16

题目思路:

任务就是要依次经过2k个点(十分的明显)

用最暴力的DP :设f[i][u][a][b]表示已经完成了前i个任务,当前在点u,两个传送门分别位于a和b的最短距离。

转移有三种:

在u设立传送门
从u走到一个相邻节点
如果u等于a或者b,那么可以使用传送门

这个DP的转移显然是有环的,因此需要使用Dijkstra算法,但用floyd一样能过

仔细观察,发现记录两个传送门的位置是没有用的,只需要记录一个。因为如果我们要使用一个传送门, 一定是走到那个节点再使用(我们可以随时在当前节点创建传送门)。

设f[i][u][p]表示当前已经完成了前i个任务,在节点u,其中一个传送门位于点p。

转移有4种:

从u走到相邻节点
在u设置传送门,令p=u
从u传送到p
在u设置传送门,传送到p,并只保留u的传送门(交换u和p)

再仔细观察,发现可以继续精简状态,设c[i]表示第i个任务的节点。

设f[i][p]表示当前已经完成了前i个任务,当前正在c[i]上,传送门的位置在p。

可以证明,只需要3种转移,就可以覆盖所有情况:

直接从c[i]走到c[i+1]
枚举走到c[i+1]之后,传送门的位置变为了哪个节点,设这个节点是q。第二种转移是从c[i]走到q,在q设置传送 门,从q传送到p,再从p走到c[i+1]
第三种转移是从c[i]传送到p,从p走到q,在q设置传送门,最后从q走到c[i+1]

上述的复杂度为O(kn^2),勉强能过。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,k;
long long l[700];
long long ans=100000000000000000;
long long dp[700][700];
long long dis[400][400],w[400][400];
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++) w[i][j]=1e18+7,dis[i][j]=1e18+7;
        dis[i][i]=0;
    }
    long long x,y,z;
    for(int i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld",&x,&y,&z);
        w[x][y]=z;
        w[y][x]=z;
        dis[x][y]=dis[y][x]=min(dis[x][y],z);
    }
    for(int i=1;i<=k;i++)
    {
        scanf("%lld%lld",&x,&y);
        l[i*2]=y;
        l[i*2-1]=x;
    }
    for(int v=1;v<=n;v++)
    for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
    dis[i][j]=min(dis[i][j],dis[i][v]+dis[v][j]);
    for(int i=0;i<=2*k;i++)
    for(int j=0;j<=n;j++)
    dp[i][j]=100000000000000000;
    l[0]=1;
    dp[0][1]=0;
    for(int i=0;i<2*k;i++)
    for(int j=1;j<=n;j++)
    {
        dp[i+1][j]=min(dp[i+1][j],dp[i][j]+dis[l[i]][l[i+1]]);
        dp[i+1][j]=min(dp[i+1][j],dp[i][j]+dis[j][l[i+1]]);
        for(int v=1;v<=n;v++)
    	dp[i+1][v]=min(dp[i+1][v],dp[i][j]+min(dis[l[i]][v],dis[j][v])+min(dis[j][l[i+1]],dis[v][l[i+1]]));
    }
    for(int i=1;i<=n;i++)
    ans=min(ans,dp[2*k][i]);
    printf("%lld",ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值