Codeforces 1065D Three Pieces

简述题意:

算法:记忆化搜索+dp

难度:NOIP+

题解:

我们采用记忆化搜索的方法

dp[i][j][z][t][k]表示的是到达mapp[i][j]这个点,采用上述3种走法的第z种,已经更换了t次走法(第二个ans),已经按照顺序访问到了编号为k这个点时,所花费的最小代价。

然后,我们就可以快乐地去分类讨论,记忆化bfs了。

注意:我们记忆化搜索的时候,如果dp值已经被更新过,那么就不需要再次被更新了,想想这是为什么!

我们其实可以发现,队列中所有状态的dp值其实我们已经保证了单调递增,所以如果我们如果在此时刻已经对某一个状态进行了转移,那么下次如果还想要转移此状态,那么再次转移到此状态的dp值一定大于等于此状态dp已有的值!

最后统计答案时,要注意,第一次先找到最小的花费ans,第二次由于有可能有许多的t值满足答案为ans,这时我们选择第一层循环从小到大枚举t,这样,如果找到了与ans相等的dp值,即可输出这时的t,一定的最合法(小)的解。

伪代码如下:

int ans=0x3f3f3f3f; 
    for(int t = 0;t <= 205;t++)
    {
    	for(int z = 1;z <= 3;z++)
		{
            if(dp[dex][dey][z][t][n*n]!=-1)
            {
            //	cout << dp[dex][dey][z][t][n*n] << " ";
    			ans = min(dp[dex][dey][z][t][n*n],ans);
    		}
    	}
    	//puts("");
	}
	for(int t = 0;t <= 205;t++)//一定要先枚举t,因为我们已经统计出最小的ans,这时需要保证t最小!!! 
 	{
	    for(int z = 1;z <= 3;z++)
	    {
            if(dp[dex][dey][z][t][n*n]==ans)
            {
            	//cout << t << " ";
    			printf("%d %d\n",ans,t);
    			exit(0);
    		}
        }
    }

注意:细节超级多!!!

代码如下:

#include <cstdio>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <queue>
#define ll long long
#define N 15
using namespace std;
/*bfs搜索,状态可以这么设定dp[i][j][z][t][k]表示到达g[i][j]这个点,
用的是第z种走法,已经换了t次走法,已经按顺序访问了k个点,此时的最小精力值。*/
struct node
{
    int x,y,k,t,z;
    node(int x_x,int y_y,int k_k,int t_t,int z_z)
    {
        x=x_x;
        y=y_y;
        z=z_z;
        k=k_k;
        t=t_t;//刚刚把=打成-,莫名 MLE!!!w 
    }
};
int dp[12][12][5][N*N][N*N];
int dx1[8][2]={{-2,-1},{-2,1},{2,-1},{2,1},{-1,-2},{-1,2},{1,-2},{1,2}}; //马 
int dx2[4][2]={{-1,0},{1,0},{0,1},{0,-1}}; //车 
int dx3[4][2]={{-1,-1},{-1,1},{1,-1},{1,1}}; //相 
int mapp[N][N];
int n;
void bfs(int q,int w)
{
    dp[q][w][1][0][1]=0;
    dp[q][w][2][0][1]=0;
    dp[q][w][3][0][1]=0;
    queue<node>Q;
    Q.push(node(q,w,1,0,1));
    Q.push(node(q,w,1,0,2));
    Q.push(node(q,w,1,0,3));
    while(!Q.empty())
    {
        node u=Q.front();
        Q.pop();
        int x=u.x,y=u.y,z=u.z,k=u.k,t=u.t;
        for(int i = 1;i <= 3;i++)
        {
            if(i==z) continue;
            if(dp[x][y][i][t+1][k]!=-1) continue;
            dp[x][y][i][t+1][k]=dp[x][y][z][t][k]+1;//刚刚把z打成了i????..... 
            Q.push(node(x,y,k,t+1,i));
        }
        if(z==1)
        {
            for(int i = 0;i < 8;i++)
            {
                int xx=x+dx1[i][0],yy=y+dx1[i][1],kk=k;
                if(xx<1||xx>n||yy<1||yy>n) continue;
                if(mapp[xx][yy]==k+1) kk++;
                if(dp[xx][yy][z][t][kk]!=-1) continue;
                dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
                Q.push(node(xx,yy,kk,t,z));
            }
        }
        if(z==2)
        {
            for(int j = 1;j <= 10;j++)
            {
                for(int i = 0;i < 4;i++)
                {
                    int xx=x+j*dx2[i][0],yy=y+j*dx2[i][1],kk=k;
                    if(xx<1||xx>n||yy<1||yy>n) continue;
                    if(mapp[xx][yy]==k+1) kk++;
                    if(dp[xx][yy][z][t][kk]!=-1) continue;
                    dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
                    Q.push(node(xx,yy,kk,t,z));
                }
            }
        }
        if(z==3)
        {
            for(int j = 1;j <= 10;j++)
            {
                for(int i = 0;i < 4;i++)
                {
                    int xx=x+j*dx3[i][0],yy=y+j*dx3[i][1],kk=k;
                    if(xx<1||xx>n||yy<1||yy>n) continue;
                    if(mapp[xx][yy]==k+1) kk++;
                    if(dp[xx][yy][z][t][kk]!=-1) continue;
                    dp[xx][yy][z][t][kk]=dp[x][y][z][t][k]+1;
                    Q.push(node(xx,yy,kk,t,z));
                }
            }
        }
    }
}
int main()
{
    //printf("%d\n",sizeof(dp)/1024/1024); 
    scanf("%d",&n);
    int stx,sty,dex,dey;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 1;j <= n;j++)
        {
            int x;
            scanf("%d",&x);
            mapp[i][j]=x;
            if(x==1) stx=i,sty=j;
            if(x==n*n) dex=i,dey=j;
        }
    }
    memset(dp,-1,sizeof(dp));
    bfs(stx,sty);
    int ans=0x3f3f3f3f; 
    for(int t = 0;t <= 205;t++)
    {
    	for(int z = 1;z <= 3;z++)
		{
            if(dp[dex][dey][z][t][n*n]!=-1)
            {
            //	cout << dp[dex][dey][z][t][n*n] << " ";
    			ans = min(dp[dex][dey][z][t][n*n],ans);
    		}
    	}
    	//puts("");
	}
	for(int t = 0;t <= 205;t++)//一定要先枚举t,因为我们已经统计出最小的ans,这时需要保证t最小!!! 
 	{
	    for(int z = 1;z <= 3;z++)
	    {
            if(dp[dex][dey][z][t][n*n]==ans)
            {
            	//cout << t << " ";
    			printf("%d %d\n",ans,t);
    			exit(0);
    		}
        }
    }
    return 0 ;
} 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值