noip2010乌龟棋解题报告

                           乌龟棋解题报告
题目描述 Description
小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一 的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。
…… 1 2 3 4 5 ……N 乌龟棋中M张爬行卡片,分成4种不同的类型(M张卡片中不一定包含所有4种类型 的卡片,见样例),每种类型的卡片上分别标有1、2、3、4四个数字之一,表示使用这种卡 片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择 一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。 游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到 该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的 分数总和。 很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡 片使用顺序使得最终游戏得分最多。 现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到 多少分吗?
输入描述 Input Description
输入的每行中两个数之间用一个空格隔开。 第1行2个正整数N和M,分别表示棋盘格子数和爬行卡片数。 第2行N个非负整数,a1a2……aN
,其中ai表示棋盘第i个格子上的分数。 第3行M个整数,b1b2……bM
,表示M张爬行卡片上的数字。 输入数据保证到达终点时刚好用光M张爬行卡片,即N - 1=∑(1->M) bi
输出描述 Output Description
输出一行一个整数
样例输入 Sample Input
13 8
4 96 10 64 55 13 94 53 5 24 89 8 30
1 1 1 1 1 2 4 1
样例输出 Sample Output
455
数据范围及提示 Data Size & Hint
【数据范围】
对于30%的数据有1 ≤ N≤ 30,1 ≤M≤ 12。
对于50%的数据有1 ≤ N≤ 120,1 ≤M≤ 50,且4 种爬行卡片,每种卡片的张数不会超
过20。
对于100%的数据有1 ≤ N≤ 350,1 ≤M≤ 120,且4 种爬行卡片,每种卡片的张数不会
 
典型的背包型动规,动规方程为
a[i,j,k,l]=max(a[i-1,j,k,l],a[i,j-1,k,l],a[i,j,k-1,l],a[i,j,k,l-1])+step[i+j*2+k*3+l*4+1]

核心程序
    for(i=0;i<=sum[1];i++)  
        for(j=0;j<=sum[2];j++)  
            for(k=0;k<=sum[3];k++)  
                for(t=0;t<=sum[4];t++)  
                {  
                    if(i>=1)
                      if(f[i][j][k][t]<f[i-1][j][k][t]+a[1+i*1+j*2+k*3+t*4])  
                         f[i][j][k][t]=f[i-1][j][k][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(j>=1)
                      if(f[i][j][k][t]<f[i][j-1][k][t]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j-1][k][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(k>=1)
                        if(f[i][j][k][t]<f[i][j][k-1][t]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j][k-1][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(t>=1)
                        if(f[i][j][k][t]<f[i][j][k][t-1]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j][k][t-1]+a[1+i*1+j*2+k*3+t*4];
                }

刚开始看这道题的时候想到
用搜索的方法把所有可能的排列枚举出来,再求其最优。不过,明显的看出,这样只能过那30%的简单数据,见到另外70%的数据,12个以上的卡片,估计就要超时了
所以用动规则可以过100%数据。
在我的动规算法中,状态之一是每种卡片使用的情况,状态之二是当前状态可以所得的最大分数。

开数组sum[  ]来存储四种卡片的个数,其下标表示卡片的使用效果,即用一张该种卡片可以走几格。

    开数组a[  ]来存储每一格的分数情况,其下标表示该格是第几格,以a[ 1 ]为第一格。

    开数组f[ i ][ j ][ k ][ t ]来存储用某种方式所得的最大分数,其中i,j,k,t分别表示当前第一,二,三,四种卡片的使用张数,则其

初始状态为f[ 0 ][ 0 ][ 0 ][ 0 ](= a[ 1 ]),

目标状态为f[   sum[ 1 ]   ][   sum[ 2 ]   ][   sum[ 3 ]   ][   sum[ 4 ]   ]。

动态状态转移规则为:
a[ i ][ j ][ k ][ t ] = a[ t*4+k*3+j*2+i+1 ] +
max{  a[ i-1 ][ j ][ k ][ t ]  ,
          a[ i ][ j-1 ][ k ][ t ]  ,
          a[ i ][ j ][ k-1 ][ t ]  ,
          a[ i ][ j ][ k ][ t-1 ] 
      }
 
只要推出这个方程这道题目就没什么难度了
#include<stdio.h>  
  
int n,m,a[400],sum[5];  
int f[50][50][50][50];  
  
int main()  
{  
    scanf("%d%d",&n,&m);  
    int i,j,k,t,x;  
    for(i=1;i<=n;i++) scanf("%d",&a[i]);  
    for(i=1;i<=m;i++) {scanf("%d",&x);sum[x]++;}  
    f[0][0][0][0]=a[1];  
    for(i=0;i<=sum[1];i++)  
        for(j=0;j<=sum[2];j++)  
            for(k=0;k<=sum[3];k++)  
                for(t=0;t<=sum[4];t++)  
                {  
                    if(i>=1)
                      if(f[i][j][k][t]<f[i-1][j][k][t]+a[1+i*1+j*2+k*3+t*4])  
                         f[i][j][k][t]=f[i-1][j][k][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(j>=1)
                      if(f[i][j][k][t]<f[i][j-1][k][t]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j-1][k][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(k>=1)
                        if(f[i][j][k][t]<f[i][j][k-1][t]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j][k-1][t]+a[1+i*1+j*2+k*3+t*4];  
                    if(t>=1)
                        if(f[i][j][k][t]<f[i][j][k][t-1]+a[1+i*1+j*2+k*3+t*4])
                        f[i][j][k][t]=f[i][j][k][t-1]+a[1+i*1+j*2+k*3+t*4];
                }  
    printf("%d",f[sum[1]][sum[2]][sum[3]][sum[4]]);  
    return 0;  
}  
    

 

posted @ 2013-09-24 00:22  ghicefox  阅读(348)  评论(0编辑  收藏  举报