湖南省第六届大学生计算机程序设计竞赛I - 射击游戏

I - 射击游戏


 

A和B在玩一个射击游戏,战场由若干单位正方形积木组成。积木占据了连续的若干列,且图形周长等于它最小包围矩形的周长。下图(a)是一个合法的战场,但(b)和(c)都不是:(b)中有空列;(c)的图形周长为14,而最小包围矩形(用虚线画出)的周长为12。受重力影响,每个积木的正下方要么是地面,要么是另一个积木。为了让战场看上去错落有致、玩着更刺激,它不能恰好是一个矩形(即:不能每列积木都一样高)。

游戏规则如下:

1、 A和B轮流射击,A先射击。

2、 每次射击时,首先选择一行(该行必须至少有一个积木),以及“左”和“右”中的一个方向,然后往这个方向开火。子弹的威力为1~3的均匀随机整数(即:威力为1、2、3的概率各为1/3),表示子弹能打掉的积木个数,被打掉的积木将直接从战场中消失。如果该行的积木个数小于威力值,则子弹将在打掉该行所有积木后消失。例如,若选择往右射击从下往上数第3行,且威力为2,且这一行一共有4个积木,则最左边的两个积木将被打掉。注意:这两个积木可以不连续。

3、 每次射击完成后,悬空的积木垂直往下落。所有积木不再下落后,下一位选手才能开始射击。

4、 谁打掉了最后一个积木,谁就获胜。

假定开局是,根据规则1,A先开火。射击后,B可能面临的后续局面中的其中三个如下表:

行编号(从下往上数)

子弹前进方向

威力(随机值)

刚射击后

积木稳定后

2

从右往左

1

(同左图)

1

从右往左

2

1

从左往右

3


假定A和B都足够聪明,采取让自己获胜概率尽量高的策略,你的任务是计算出A获胜的概率。

Input
输入文件最多包含25组测试数据,每个数据仅包含两行,第一行是整数n(1<=n<=6),即积木的列数。第二行包含n个正整数h1, h2,..., hn(1<=hi<=6),表示从左往右数第i列的高度。积木的排列方式保证符合题目描述(即:图形周长等于它最小包围矩形的周长,且各列的高度不全相同)。n=0表示输入结束,你的程序不应当处理这一行。
Output
对于每组数据,输出仅一行,即A获胜的概率,四舍五入保留六位小数。
Sample Input
3
2 1 1
0
Sample Output
0.555556
Hint
代码:
#include<iostream>
#include<cstdio>
using namespace std;
#define X(A,B) (A[B[0]][B[1]][B[2]][B[3]][B[4]][B[5]])
double cache[7][7][7][7][7][7];//概率
bool vis[7][7][7][7][7][7];//统计
double win(int h[6])
{
    if(X(vis,h))
    return X(cache,h);
    X(vis,h)=true;
    X(cache,h)=0.0;
    int  mx=0;
    for(int i=0;i<6;i++)
        mx=max(mx,h[i]);//mx为最高列
    int i=0,hh[6];
    for(int j=0;j<mx;j++)
    {
        for(int d=0;d<2;d++)
        {
            i=5*d;
            double loss=0;
            for(int p=1;p<=3;p++)
            {
                for(int k=0;k<6;k++)
                    hh[k]=h[k];
                int pp=p;
                int k=i;
                while(pp)
                {
                if(k<0||k>5)
                    break;
                if(hh[k]>j)
                hh[k]--,pp--;
                k+=1-2*d;
                }
                loss+=win(hh);
            }
        loss=(1-loss/3.0);
        if(loss>X(cache,h))
        X(cache,h)=loss;
      }
  }
  return X(cache,h);
}
int main()
{ int n;
  while(scanf("%d",&n)==1&&n)
  {
    int h[6]={0};
    for(int i=0;i<n;i++)
        scanf("%d",&h[i]);
    printf("%.6lf\n",win(h));
  }
  return 0;
}

用DDraw实现射击游戏说明文档 要点一:画图自动切割 IDirectDrawSurface7::BltFast()方法中没有自动切割功能,即当画图元素超出窗口以外时不会自动切割,DDraw选择自动忽略不画,造成一旦超出窗口,画图元素会突然消失。 解决这一问题的方法是手动切割,代码如下: //自动切割 RECT scRect; //存放当前窗口大小区域 ZeroMemory( &scRect, sizeof( scRect ) ); GetWindowRect( GetActiveWindow(), &scRect ); //防止图片左上角超过窗口左上角 if ( x < 0 ) { m_rect.left -= x; x = 0; } if ( y scRect.right ? scRect.right : x; y = y > scRect.bottom ? scRect.bottom : y; m_rect.right = x + m_rect.right - m_rect.left > scRect.right ? scRect.right - x + m_rect.left : m_rect.right; m_rect.bottom = y + m_rect.bottom - m_rect.top > scRect.bottom ? scRect.bottom - y + m_rect.top : m_rect.bottom; 只需将上述代码加在CGraphic::BltBBuffer() 中的m_bRect = m_rect; 前即可。 要点二:背景的滚轴实现 画背景可以分为以下三种情况: 情况一:背景图片与窗口等高 情况二:背景图片高度小于窗口高度 情况三:背景图片高度大于窗口高度 上述讲解图与代码相对应地看,有助于容易理解。 另外,要点一实现之后,由于已经可以自动切割,画背景可以用其它方法。 要点三:精灵图的实现 在游戏中,如RPG游戏中的人物图、射击类游戏的飞机、爆炸等,叫做精灵图。 精灵图实际上是将所有帧的图片放在一个文件中,游戏时靠一个RECT来控制画图像文件中的哪一部分,进而控制游戏显示哪一帧图,只需控制好RECT的位置即可。如下图: 控制RECT的四个角的坐标的移动,有以下代码: if (m_timeEnd – m_timeStart > 100) //只有到了100ms之后才绘图 { m_ImageID++; if(m_ImageID - m_beginID >= num) { m_ImageID = m_beginID; //最后一帧的下一帧是第一帧 } m_timeStart = timeGetTime(); } int id = m_ImageID++; SetRect(&m_rect, 41 * id, 0, 41 * (id + 1), 41); //飞机精灵图大小是41×41 m_pGraph->BltBBuffer(m_pImageBuffer, true, m_Pos.x, m_Pos.y, m_rect); 这样就实现了精灵动画的效果。 要点四:拿STL进行子弹的实现 子弹的实现可以使用STL中的vector,当按下开火键时发出一颗子弹,就往vector中添加一个结点;当子弹飞出窗口或击中敌机时,再将结点从vector中删除。每帧游戏画面中子弹飞行时只需将vector中的所有子弹进行处理、绘画即可。 参考代码如下: 1.添加子弹 if (g_ctrlDown) //当ctrl键按下时开炮! { m_BulletEnd = m_Gtime->GetTime(); if ((m_BulletEnd - m_BulletStart) * 1000 > 120) //如果连续按着开火键不放,这里控制不会发出太多子弹 { m_BulletStart = m_BulletEnd; MBULLET tmpBullet; tmpBullet.pos.x = m_SPos.x - 1; //记录开火时的子弹位置 tmpBullet.pos.y = m_SPos.y - 26; tmpBullet.speed = 5; //该子弹的飞行速度 m_BulletList.push_back(tmpBullet); //将子弹添加到vector中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值