牛客网Graph

题目描述:

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 aia_iai, picking a block and then sending it to vertex bib_ibi. 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.

输入描述:

输出描述:

示例1:

说明:

示例1的解决方案:从1步行到5,经过时在2和4创建门户。 然后从5步行到4,然后您可以使用门户网站完成第二个任务。

示例2:

扫描二维码关注公众号,回复: 11447169 查看本文章

题目思路:

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

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

转移有三种:

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

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

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

 设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=1e18+7;
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]=1e18+7;
    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;
}
View Code

猜你喜欢

转载自www.cnblogs.com/WWWZZZQQQ/p/13380287.html
今日推荐