poj2315足球游戏

这篇博客探讨了一种足球射门比赛的博弈问题,其中两名球员爱丽丝和鲍勃轮流射门,每次可以选择不超过M个足球,每个足球最大射程为L。问题转化为石子堆博弈,通过半加运算的异或操作确定胜负策略。最终,通过编程解决该问题,输出获胜者的名字。

问题描述:

爱丽丝和鲍勃都非常热爱足球,两人都是先锋。他们都擅长足球控制。一场足球比赛后的一天,他们进行了一场有趣的比赛,他们将足球直接射向球门。球门前有N个足球,他们轮流进行这场比赛。例如,如果轮到爱丽丝,爱丽丝可以选择一些足球(足球的数量必须等于或小于 M)并将它们射向前方。由于足球的质量不是很好,足球不是一个完整的球体,只能滚动其周长的整数倍。而且由于他们的摩擦力和力量的限制,他们无法将足球射出超过L厘米的距离。当然,他们知道足球的半径是 R 厘米。爱丽丝和鲍勃非常喜欢这个游戏。如果他们俩都拥有无限的智商和精准的射门技巧,你能猜出谁能赢得这场足球比赛吗?顺便说一句,虽然爱丽丝和鲍勃一样强壮,但爱丽丝是个女孩,所以她会先出手。

分析:题目大意是两名球员轮流从N个球中挑出不多于M个射门,每个球半径都是R,离球门S。每次只能踢出L以内的距离。进最后一个球者胜,求谁有必胜策略?我们发现对数据进行处理之后,题目等价于给出n堆石子,每堆石子中每次最多取k个石子,每次最多选取m个石子堆做操作的博弈问题,首先我们将每堆石子堆对k+1取模简化运算,取一堆石子上的石子的做法我们是对所有的石子堆的sg值进行xor运算得到sg值,xor又称为半加运算,只进行加法而不进位,对于选取m堆石子的博弈我们的xor则是对于m+1进制下的半加运算,所以我们按位计算这个sg值,模拟m+1进制下的半加运算即可得到答案。

输入:

输入由几个案例组成,每个案例包含两行。
对于每个测试用例,第一行包含 4 个整数 N、M、L 和 R(1 <= M <= N <= 30, 0 < L < 100000000, 0 < R < 10000),用一个空格分隔。N是足球的数量,M是一名球员一回合可以射出的最大足球数,L是一名球员可以射出的最大距离,R是足球的半径。
下一行包含 N 个数字,S(1), S(2), ..., S(N) (0 < S(i) < 100000000),它们描述了足球和球门之间的距离。

输出:

对于每种情况,输出都包含一行描述获胜者的姓名。

样本输入:

 

样本输出:

 程序代码:

#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
const int N=30;
const double PI=acos(-1.0);
int n,m,l,r,a[N],sg[N];
int dis(int s)                        //看能否进球
{
     return (int)(s/(2*PI*r))+1;
}
bool solve(){                        //博弈函数
    memset(sg,0,sizeof(sg));
    int k=dis(l);
    for(int i=0;i<n;i++)
      for(int j=0,g=dis(a[i])%k;sg[j]+=g&1,g;j++,g>>=1);
        for(int i=0;i<30;i++)
           if(sg[i]%(m+1))
               return 1;
    return 0;
}
int main(){
    while(~scanf("%d%d%d%d",&n,&m,&l,&r)){
        for(int i=0;i<n;i++)
             scanf("%d",&a[i]);
             puts(solve()?"Alice":"Bob");
    }
    return 0;
}

yjg

POJ1390 方盒游戏是一个动态规划问题,其核心是通过升维(三维)的方式来解决。以下是使用 C 语言解决该问题的思路和代码示例: ### 思路 - 首先明确问题,N 个方盒摆成一排,连续相同颜色的方盒构成一个方盒片段,每次点击一个方盒,该方盒所在片段消失,若片段有 k 个方盒,玩家获得 $k^2$ 个积分,目标是求出最大积分。 - 采用三维动态规划,定义状态 `dp[i][j][k]` 表示从第 `i` 个片段到第 `j` 个片段,且第 `j` 个片段后面连续有 `k` 个与第 `j` 个片段颜色相同的方盒时能获得的最大积分。 - 状态转移: - 情况一:直接消除第 `j` 个片段及其后面的 `k` 个同色方盒,即 `dp[i][j][k] = dp[i][j - 1][0] + (len[j] + k) * (len[j] + k)`,其中 `len[j]` 表示第 `j` 个片段的方盒数量。 - 情况二:在 `i` 到 `j - 1` 中找到一个与第 `j` 个片段颜色相同的片段 `p`,将第 `p + 1` 到 `j - 1` 个片段先消除,然后将第 `p` 个片段和第 `j` 个片段及其后面的 `k` 个同色方盒合并消除,即 `dp[i][j][k] = max(dp[i][j][k], dp[i][p][len[j] + k] + dp[p + 1][j - 1][0])`。 ### 代码示例 ```c #include <stdio.h> #include <string.h> #define MAXN 205 int color[MAXN], len[MAXN]; int dp[MAXN][MAXN][MAXN]; // 比较两个整数大小,返回较大值 int max(int a, int b) { return a > b ? a : b; } // 动态规划求解最大积分 int solve(int i, int j, int k) { if (dp[i][j][k] != -1) return dp[i][j][k]; if (i > j) return 0; // 情况一:直接消除第 j 个片段及其后面的 k 个同色方盒 dp[i][j][k] = solve(i, j - 1, 0) + (len[j] + k) * (len[j] + k); // 情况二:寻找中间与第 j 个片段颜色相同的片段 for (int p = i; p < j; p++) { if (color[p] == color[j]) { dp[i][j][k] = max(dp[i][j][k], solve(i, p, len[j] + k) + solve(p + 1, j - 1, 0)); } } return dp[i][j][k]; } int main() { int T, n, cases = 1; scanf("%d", &T); while (T--) { scanf("%d", &n); int cnt = 0; for (int i = 0; i < n; i++) { int c; scanf("%d", &c); if (i == 0 || c != color[cnt - 1]) { color[cnt] = c; len[cnt] = 1; cnt++; } else { len[cnt - 1]++; } } memset(dp, -1, sizeof(dp)); int ans = solve(0, cnt - 1, 0); printf("Case %d: %d\n", cases++, ans); } return 0; } ``` ### 代码解释 1. **输入处理**:首先读取测试用例的数量 `T`,对于每个测试用例,读取方盒的数量 `n`,并将连续相同颜色的方盒合并为一个片段,记录每个片段的颜色和长度。 2. **动态规划数组初始化**:将 `dp` 数组初始化为 -1,表示该状态还未计算。 3. **状态转移**:在 `solve` 函数中实现状态转移,根据上述两种情况更新 `dp` 数组。 4. **输出结果**:对于每个测试用例,输出最大积分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值