1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——Mobile Service

藍書(演算法競賽進階指南)刷題記錄——Mobile Service

題目大意:三個人一開始在1,2,3的位置,每次給定一個1~l的座標(必須按順序),他們之中必須有一個人走到這個座標,且他們之中不能有兩人站在同一位置,從點x走到點y的花費為c(x,y),求最小花費.

首先這是一道十分裸的DP,我們可以設狀態f[i][x][y][z]為走過前i個給定座標,且當前第一個人在位置x,第二個人在位置y,第三個人在位置z的最小花費.

考慮三個人的走法,設第i個座標為go[i],我們可以很容易推出方程:

f[i+1][go[i+1]][y][z]=min(f[i][x][y][z]+c(x,go[i+1]))

f[i+1][x][go[i+1]][z]=min(f[i][x][y][z]+c(y,go[i+1]))

\bg_white f[i+1][x][y][go[i+1]]=min(f[i][x][y][z]+c(z,go[i+1]))

這個演算法的時空複雜度為O(nl^3),TLE+MLE.

我們發現,當一個狀態f[i]是有意義的情況下,必定有一個人的位置為go[i].而直到三個人的座標不需要知道順序,所以我們的狀態可以簡化成f[i][x][y]表示走過前i個給定座標,有一個人的座標為x,有一個人的座標為y,有一個人的座標為go[i]即可.

那麼我們設go[0]為3,然後初始化f[0][1][2]=f[0][2][1]=0.

方程如同上面,分別推斷三個情況可以得出:

f[i+1][x][y]=min(f[i][x][y]+c(go[i],go[i+1]))

f[i+1][go[i]][y]=min(f[i+1][x][y]+c(x,go[i+1]))

f[i+1][x][go[i]]=min(f[i+1][x][y]+c(y,go[i+1]))

那麼這道題其實就這樣可以AC了,時間複雜度O(nl^2),空間複雜度O(nl^2)若加上滾動陣列可以進一步優化空間複雜度為O(l^2).

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
const int L=200,N=1000;
const int INF=(1<<30)-1;
int l,dis[L+9][L+9];
int n,go[N+9];
int ans,f[N+9][L+9][L+9];
void getmin(int &a,int b){
  a=a<b?a:b;
}
Abigail into(){
  scanf("%d%d",&l,&n);
  for (int i=1;i<=l;i++)
    for (int j=1;j<=l;j++)
      scanf("%d",&dis[i][j]);
  for (int i=1;i<=n;i++)
    scanf("%d",&go[i]);
}
Abigail work(){
  for (int i=0;i<=N+1;i++)
    for (int j=0;j<=L+1;j++)
      for (int k=0;k<=L+1;k++)
        f[i][j][k]=INF;
  int x,y;
  go[0]=3;
  f[0][1][2]=0;f[0][2][1]=0;
  for (int i=0;i<n;i++)
    for (int j=1;j<=l;j++)
      for (int k=1;k<=l;k++){
        x=go[i];y=go[i+1];
        if (j^y&&k^y&&j^k) getmin(f[i+1][j][k],f[i][j][k]+dis[x][y]);
        if (x^y&&k^y&&x^k) getmin(f[i+1][x][k],f[i][j][k]+dis[j][y]);
        if (x^y&&j^y&&x^j) getmin(f[i+1][j][x],f[i][j][k]+dis[k][y]);
      }
  ans=INF;
  for (int i=1;i<=l;i++)
    for (int j=1;j<=l;j++)
      getmin(ans,f[n][i][j]);
}
Abigail outo(){
  printf("%d\n",ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}