【Codeforces 738F】 Financiers Game【记忆化DP】

本文深入探讨了一个涉及两人轮流取数的博弈问题,通过动态规划(DP)算法实现最优策略,以最大化双方取数之差。文章详细分析了算法的空间和时间复杂度,提出了一种高效解决方案,将状态空间压缩至dp[2100][200][100][2],并提供了完整的C++代码实现。

题意:

有一个数列,两个人分别从两端开始取数字,左边的先取,第一次能取1个或2个,然后轮流取得时候,如果第一个取了k个,后一个人只能取k或k+1个,如果剩下的不够了则游戏终止,I想要最大化两个人取得数字之和的差距,Z想要最小化,两个人都选择最优操作

题解:

重点是空间时间复杂度

上述算法看起来是n3次的dp[L][R][K]

但实际上L最多只能达到2000多一点的样子(I多拿一次数字的情况下

K需要从一开始增加,从1开始增加到最大1,2,……,k sum=k*(k+1)/2 k<=sqrt(2*n)也就是90左右

在来看R,R代表的是Z先生拿到的位置,设d为I与Z取走数字数量之差,d=(n - r) - (l - 1) 取左右100就200吧

所以最终我们只需要dp[2100][200][100][2];

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;
int n;
int a[4005];
int dp[2100][200][100][2];
bool used[2100][200][100][2];
int sum[4005];

int dfs(int l,int d,int k,int t)
{
    if(used[l][d][k][t]!=0)
        return dp[l][d][k][t];

        used[l][d][k][t]=1;
        int dis=d-100;
        int cntr=l+dis;
        int r=n+1-cntr;

        int tans;
        if(t==0)
        {
            if(l+k<r)
            {
                tans=dfs(l+k,d-k,k,1);
                if(l+k+1<r)
                {
                    tans=max(tans,dfs(l+k+1,d-k-1,k+1,1));
                }
            }
            else
            {
                tans=(sum[l]-sum[0])-(sum[n]-sum[r-1]);
            }
        }
        else
        {
            if(r-k>l)
            {
                tans=dfs(l,d+k,k,0);
                if(r-k-1>l)
                {
                    tans=min(tans,dfs(l,d+k+1,k+1,0));
                }
            }
            else
            {
                tans=(sum[l]-sum[0])-(sum[n]-sum[r-1]);
            }
        }
        return (dp[l][d][k][t]=tans);
}

int main()
{
    while(~scanf("%d",&n))
    {
        memset(used,0,sizeof(used));
        //cout<<dp[123][32][34][1]<<endl;
        sum[0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            sum[i]=sum[i-1]+a[i];
        }

        int ans=dfs(0,100,1,0);
        printf("%d\n",ans);

    }
}

 

Codeforces 2123F 问题中,目标是通过重新排列数组 $ a $ 来最小化不动点的数量。所谓“不动点”是指在重新排列后的数组中满足 $ a_i = i $ 的位置。该问题要求设计一种策略,以最优方式重新排列数组元素,使得这样的不动点数量最少。 为了解决这个问题,可以采用贪心算法和图论思想相结合的策略: - 首先,观察到如果某个值 $ i $ 出现了多次(即 $ a_i = i $),那么这些重复的值必须被移动到其他位置,以消除不动点。 - 对于那些没有出现在其索引上的值(例如 $ a_i \neq i $),可以通过交换操作将其移动到合适的位置,从而避免产生新的不动点。 一个有效的解决方案可以基于以下步骤: 1. 构建一个映射表,记录每个值出现的位置。 2. 找出所有当前值等于其索引的位置(即当前的不动点)。 3. 尝试通过交换来消除这些不动点。优先考虑将这些值移动到未被占用的位置,并确保不会引入新的不动点。 4. 在无法完全消除所有不动点的情况下,选择最优的交换策略以尽可能减少不动点的数量。 以下是 Python 中的一个示例实现,用于解决此类问题的基本思路: ```python def minimize_fixed_points(n, a): pos = {} fixed_points = [] # 记录每个值的出现位置,并找出初始的不动点 for i in range(n): if a[i] == i + 1: fixed_points.append(i) if a[i] not in pos: pos[a[i]] = [] pos[a[i]].append(i) # 如果没有重复的值,则可以直接交换以消除所有不动点 result = a[:] for i in fixed_points: found = False for val in pos: if val != i + 1 and len(pos[val]) > 0: j = pos[val].pop() result[i], result[j] = result[j], result[i] found = True break if not found: # 特殊情况处理:当只剩下一个值时 for j in range(n): if j != i and result[j] != j + 1: result[i], result[j] = result[j], result[i] break return result # 示例输入 n = int(input()) a = list(map(int, input().split())) result = minimize_fixed_points(n, a) print(' '.join(map(str, result))) ``` 此代码实现了上述逻辑,并尝试通过交换来最小化不动点的数量。对于大多数情况,它能够有效消除所有不动点;在某些特殊情况下(例如所有值都唯一且存在多个不动点),则需要特别处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值