VIrtuoso

两把多兰剑加个布甲鞋

导航

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,每一个格子每一种状态就形成唯一的编号

  • 怎么处理车,马,象

车:ip||jq
马: 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];
}


posted on 2018-11-10 20:39  VIrtuoso  阅读(246)  评论(0编辑  收藏  举报