乌龟棋 noip 2010

题目链接:https://www.luogu.org/problemnew/show/P1541
今天成功写过了这一题,我们是以这一题作为考试来写,一开始知道肯定是要DP的可是状态定义错了,一开始定义第几格为状态死活写不出来,又开始改状态,后面改成了卡牌数才AC了,一开始不敢这么定义,因为四重循环怕时间炸了,后面算了一下才发现四重循环看着吓人,实际上数字非常小,是可以过的。下面讲思路:
首先定义状态,状态的定义要从每一次都有关的数据去入手(前提是这一数据是变量,例如:时间,空间等)我们f[i][j][o][p]表示走一步的牌用i张,走两步的牌用j张,走三步的牌用o张,走四步的牌用p张。
这样子定义状态转移方程就比较好写。f[i][j][o][p]=max(f[i-1][j][o][p],f[i][j-1][o][p],f[i][j][o-1][p],f[i][j][o][p-1])+q[i+2*j+3*o+4*p+1] PS:q[i]存的是第i格上的分数,c++中不可以这么写,下面代码才为标准。这个的意思是用i张一步j张两步o张三步p张四步时我们可以从i少一张其他不变或j少一张或o少一张或p少一张中最大的在加上现在这一格的分数得来。至于为什么要最大呢?原因是题目要求的就是最大。
定义完后我们再讲一下怎么实现,首先读入数据,读入时就可以边处理,在读入每一张牌时可以不存这张牌,直接在这一类牌的数量再加加,这样数据就处理完了,再讲一下状态转移方程的实现,有一个细节如果在i,j,o,p中有一个为零那么要跳过这一次循环,原因很简单你当前要用的就为零了,怎么从上一类中取,这样不仅会错,还会数组越界,会RE。
然后就可以开心地输出答案了,下面上代码:

#include<iostream>
#include<cstring>
using namespace std;
int k[5],q[351],f[41][41][41][41];//q数组存的是棋盘的分数,k数组表示走i歩的牌有几张,f便是状态转移用的数组了
int main()
{
    memset(f,0,sizeof(f));
    int n,m,i,j,a,o,p,ans;
    cin >>n>>m;
    for(i=1;i<=n;i++)
    {
        cin >>q[i];//读入棋盘
    }
    for(i=1;i<=m;i++)
    {
        cin >>a;
        k[a]++;//这样可以统计一下a种牌有几张
    }
    f[0][0][0][0]=q[1];//设置边界,因为题目说一开始直接获取第一格的分数
    for(i=0;i<=k[1];i++)
    {
        for(j=0;j<=k[2];j++)
        {
            for(o=0;o<=k[3];o++)
            {
                for(p=0;p<=k[4];p++)
                {
                    if(i!=0)
                    {
                        f[i][j][o][p]=max(f[i][j][o][p],f[i-1][j][o][p]+q[i+2*j+3*o+4*p+1]);
                    }
                    if(j!=0)
                    {
                        f[i][j][o][p]=max(f[i][j][o][p],f[i][j-1][o][p]+q[i+2*j+3*o+4*p+1]);
                    }
                    if(o!=0)
                    {
                        f[i][j][o][p]=max(f[i][j][o][p],f[i][j][o-1][p]+q[i+2*j+3*o+4*p+1]);
                    }
                    if(p!=0)
                    {
                        f[i][j][o][p]=max(f[i][j][o][p],f[i][j][o][p-1]+q[i+2*j+3*o+4*p+1]);
                    }
                }
            }
        }
    }
    cout <<f[k[1]][k[2]][k[3]][k[4]];
    return 0;
 } 

代码是拿来看的而不是抄袭,希望大家在看完这篇题解后可以自己尝试写一遍,祝大家都可以AC

猜你喜欢

转载自blog.csdn.net/qq_34990731/article/details/81487825