Noip2009模拟题_打砖块(game)

本文深入探讨了打砖块游戏中的策略选择与得分最大化问题,通过动态规划算法解决,详细解析了如何利用游戏规则,特别是针对特殊砖块(如Y型砖块)的得分累积与额外子弹获取机制,最终实现最优得分策略。
打砖块(game.pas/c/cpp)


【题目描述】


        小红很喜欢玩一个叫打砖块的游戏,这个游戏的规则如下:
        在刚开始的时候,有n行*m列的砖块,小红有k发子弹。小红每次可以用一发子弹,打碎某一列当前处于这一列最下面的那块砖,并且得到相应的得分。
        如图所示:
 


        某些砖块在打碎以后,还可能将得到一发子弹的奖励。最后当所有的砖块都打碎了,或者小红没有子弹了,游戏结束。
        小红在游戏开始之前,就已经知道每一块砖在打碎以后的得分,并且知道能不能得到一发奖励的子弹。小红想知道在这次游戏中她可能的最大得分,可是这个问题对于她来说太难了,你能帮帮她吗?


【输入格式】


        第一行有3个正整数,n,m,k。表示开始的时候,有n行*m列的砖块,小红有k发子弹。
        接下来有n行,每行的格式如下:
        f1 c1 f2 c2 f3 c3 …… fm cm
        其中fi为正整数,表示这一行的第i列的砖,在打碎以后的得分。ci为一个字符,只有两种可能,Y或者N。Y表示有一发奖励的子弹,N表示没有。
        所有的数与字符之间用一个空格隔开,行末没有多余的空格。


【输出格式】


        仅一个正整数,表示最大的得分。


【输入样例】


        3 4 2
        9 N 5 N 1 N 8 N
        5 N 5 Y 5 N 5 N
        6 N 2 N 4 N 3 N


【输出样例】


        13


【数据规模】


        对于20%的数据,满足1<=n,m<=5,1<=k<=10,所有的字符c都为N
        对于50%的数据,满足1<=n,m<=200,1<=k<=200,所有的字符c都为N
        对于100%的数据,满足1<=n,m<=200,1<=k<=200,字符c可能为Y
        对于100%的数据,所有的f值满足1<=f<=10000


算法:DP

很值得思考的一个问题,我觉得是很有深度的一道题。
首先对于Y这个特殊情况(打一次附赠一次子弹),我们可以证明其实用1发子弹我们就相当于1发子弹都没用,假设当前子弹为x发,那么x-1+1=x,所以对于Y这种情况我们可以作连续累计的。

具体解释见代码。

program game;

const
 maxn=200;

var
 n,m,tot:longint;
 f,by,bn,sy,sn:array [0..maxn,0..maxn] of longint;{四个数组,f[i,j]表示第i行第j列的砖块的分数,by[i,j]表示前i列总共用了j发子弹获得的最大值(注意这里是总共用了子弹的数目),bn[i,j]表示前i列实际用了j发子弹获得的最大值(实际用了表示有多少用了多少),sy[i,j]表示第i列前j行获得额外子弹的分数,sn[i,j]表示不获得额外子弹的分数。}

{因为这四个数组不太容易理解,所以在这里说明一下。}

 c:array [0..maxn,0..maxn] of char;

procedure init;
var
 i,j,st:longint;
 ch:char;
begin
 readln(n,m,tot);
 for i:=1 to n do
  begin
   for j:=1 to m do read(f[i,j],ch,c[i,j]);
   readln;
  end;
 for i:=1 to m do
  begin
   st:=n;
   while (st>0) and (c[st,i]='Y') do{这里sy[i,0]相当于一发子弹都没有用,因为打一发就获得一发。}
    begin
     inc(sy[i,0],f[st,i]);
     dec(st);
    end;
   for j:=1 to n do
    begin
     if st>0 then
      begin
       sn[i,j]:=sy[i,j-1]+f[st,i];
       sy[i,j]:=sn[i,j];
       dec(st);
       while (st>0) and (c[st,i]='Y') do{同理,连续的Y实际上一发子弹都没有浪费。}
        begin
         inc(sy[i,j],f[st,i]);
         dec(st);
        end;
      end;
    end;
  end;
end;

function max(x,y:longint):longint;
begin
 if x>y then exit(x) else exit(y);
end;

procedure main;
var
 i,j,k:longint;
begin
 for i:=1 to m do
  begin
   for j:=0 to tot do
    begin
     for k:=0 to n do
      begin
       if k<=j then{这里是总限制,如果打掉前k行,那么子弹必须小于等于用过的子弹。}
        begin
         by[i,j]:=max(by[i,j],by[i-1,j-k]+sy[i,k]);{这里不做限制,因为我们表示的都是获得了额外子弹的那个分数,而打额外子弹不需要任何耗费。}
         if k<j then bn[i,j]:=max(bn[i,j],bn[i-1,j-k]+sy[i,k]);{实际用的子弹必须大于0。}
         if k>0 then bn[i,j]:=max(bn[i,j],by[i-1,j-k]+sn[i,k]);{同上。}
        end;
      end;
    end;
  end;
end;

begin
 assign(input,'game.in'); reset(input);
 assign(output,'game.out'); rewrite(output);

 init;
 main;
 writeln(bn[m,tot]);{最后输出的也是实际上用掉的子弹。}

 close(input); close(output);
end.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值