P1541 乌龟棋

题目背景

小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。

题目描述

乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。

乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型的卡片,见样例),每种类型的卡片上分别标有1,2,3,4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。

游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。

很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。

现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?

输入输出格式

输入格式:

每行中两个数之间用一个空格隔开。

第 1 行 2个正 整数N,M,分别表示棋盘格子数和爬行卡片数。

第 2 行 N个非 负整数,a1,a2,,aN,其中ai表示棋盘第i个格子上的分数。

第 3 行 M个整 数,b1,b2,,bM,表示M张爬行卡片上的数字。

输入数据保证到达终点时刚好用光M张爬行卡片。

 .

输出格式:

1个整数,表示小明最多能得到的分数。

输入输出样例

输入样例1:

9 5
6 10 14 2 8 8 18 5 17
1 3 1 2 1

输出样例1:

73

思路:

这是一道DP题。

首先我们有四种卡牌,对应着4种选择,其中每一种选择都有x种阶段,所以总的状态数是每种卡牌的数量的积。

我们用dp[a][b][c][d]表示当前状态使用的卡牌分别对应a,b,c,d张。之后注意由于是在第一张开始走的,所以当前的下表要加上初始的1。

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

long long read()
{
    long long x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,num[358];
int bb,b[5],dp[41][41][41][41];

int main()
{
     
    n=read();m=read();
    for(int i=1;i<=n;++i) num[i]=read(); 
    for(int i=1;i<=m;++i) bb=read(),b[bb]++;
    dp[0][0][0][0]=num[1];
    for(int a1=0;a1<=b[1];++a1)
        for(int a2=0;a2<=b[2];++a2)
            for(int a3=0;a3<=b[3];++a3)
                for(int a4=0;a4<=b[4];++a4)
                {
                    int now=a1+a2*2+a3*3+a4*4+1;
                    if(a1) dp[a1][a2][a3][a4]=max(dp[a1][a2][a3][a4],dp[a1-1][a2][a3][a4]+num[now]);
                    if(a2) dp[a1][a2][a3][a4]=max(dp[a1][a2][a3][a4],dp[a1][a2-1][a3][a4]+num[now]);
                    if(a3) dp[a1][a2][a3][a4]=max(dp[a1][a2][a3][a4],dp[a1][a2][a3-1][a4]+num[now]);
                    if(a4) dp[a1][a2][a3][a4]=max(dp[a1][a2][a3][a4],dp[a1][a2][a3][a4-1]+num[now]);
                } 
    printf("%d",dp[b[1]][b[2]][b[3]][b[4]]);
    return 0;
} 

其次是第一次写的错误代码:

反思:

由于本题中当前的最优解并不能导致全局的最优解,所以不能仅仅由前四种状态递推过来,否则会丢失达成最优解的状态。

WA递推代码:

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

long long read()
{
    long long x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

int n,m,num[358];
int bb,b[5];

struct SUB{
    int val;
    int s[5];
}sub[358];

int main()
{
    n=read();m=read();
    for(int i=1;i<=n;++i) num[i]=read();
    for(int i=1;i<=m;++i) bb=read(),b[bb]++;
    for(int i=1;i<=4;++i) sub[1].s[i]=b[i];
    sub[1].val=num[1];
    for(int i=2;i<=n;++i)
    {
        for(int j=4;j>=1;--j)
        {
            int now=i-j;
            if(now>=1)
            {
                if(sub[now].s[j])
                {
                    if(sub[i].val<sub[now].val+num[i])
                    {
                        sub[i]=sub[now];
                        sub[i].val+=num[i];
                        sub[i].s[j]--;
                    }
                }
            }
        }
    }
    printf("%d",sub[n].val+num[1]);
    return 0;
} 
原文地址:https://www.cnblogs.com/-hhs/p/11222494.html