1. 程式人生 > 實用技巧 >牛客網Graph

牛客網Graph

題目描述:

You are now in a big factory. The factory could be recognized as a graph withn vertices andm edges. Every edge has its length. You havek 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:

題目思路:

任務就是要依次經過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