1. 程式人生 > >D. Three Pieces(dp,array用法,棋盤模型)

D. Three Pieces(dp,array用法,棋盤模型)

https://codeforces.com/contest/1065/problem/D

題意

給你一個方陣,裡面的數字從1~nn,你需要從標號為1的格子依次走到標號為nn,在每一個格子你有兩個決策:
1.換工具(車,馬,象)
2.不換工具,繼續走
換工具本身算作一步,問最少需要多少步才能完成目標,要是步數相同,需要換工具步數最小

思路

思路十分明確,每個格子有三個狀態,處理出每個格子每個狀態之間的最小步,然後從編號為1的格子進行dp,答案就是
min(dp[id[n]][0~2])

處理

這道題難在處理

  • 怎麼保證狀態編號唯一

    首先將二維座標一維化,因為每個位置有三種狀態等於加了一維上去,所以要將一維化的座標*3,再加上0~2,每一個格子每一種狀態就形成唯一的編號

  • 怎麼處理車,馬,象

    車:i==p||j==q
    馬: abs(i-p)+abs(j-q)==3
    象:i+j==p+q||i-j==p-q
    需要注意的是,一定要用if else if 並且注意判斷順序(先判車後判馬),因為對於車的判定有的馬會在裡面,所以必須得先把車給判掉

  • 關於答案的計算

    學了一下array,可以直接比較(按下標逐個比),可以過載運算子
    還有memset可以初始化結構體,1大概是1e7

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define M 505
using namespace std;
array<int,2> f[M][M],dp[M][5],ans;
int n,i,j,k,a[M],p,q,x,y;
int id(int x,int y){return (x*n+y)*3;}

array<int,2> operator+(const array<int,2> a,const array<int,2> b){
    return {a[0]+b[0],a[1]+b[1]};
}

int main(){
    cin>>n;
    for(i=0;i<n*n;i++){cin>>x;a[x]=i;}   
    memset(f,1,sizeof(f));memset(dp,1,sizeof(dp));

    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            for(p=0;p<3;p++)for(q=0;q<3;q++){
               x=id(i,j);
               f[x+p][x+q]={1,1};
            } 
            for(p=0;p<n;p++){
                for(q=0;q<n;q++){
                   x=id(i,j);y=id(p,q);
                   if(p==i||q==j)f[x][y]={1,0};
                   else if(abs(i-p)+abs(j-q)==3)f[x+1][y+1]={1,0};
                   else if(p+q==i+j||p-q==i-j)f[x+2][y+2]={1,0};
                }
            }
        }
    }
    for(k=0;k<3*n*n;k++)for(i=0;i<3*n*n;i++)for(j=0;j<3*n*n;j++)f[i][j]=min(f[i][j],f[i][k]+f[k][j]);

    dp[1][0]=dp[1][1]=dp[1][2]={0,0};
    for(i=2;i<=n*n;i++){
        q=a[i]*3;p=a[i-1]*3;
        for(j=0;j<3;j++)
            for(k=0;k<3;k++)
                dp[i][j]=min(dp[i][j],dp[i-1][k]+f[p+k][q+j]);
    }
    ans=min(dp[n*n][0],min(dp[n*n][1],dp[n*n][2]));
    cout<<ans[0]<<" "<<ans[1];
}