首页 > 其他分享 >【小学期实训】附加题题解——最高段位

【小学期实训】附加题题解——最高段位

时间:2023-07-19 16:22:52浏览次数:39  
标签:一局 连胜 期实训 题解 样例 段位 max dp

[dp状态设计] 实训附加题——最高段位

目录

题目描述

题目链接

背景

香风智乃除了喜欢玩瓶中船之外,还喜欢打竞技游戏。

有一天她被 \(ELO\) 匹配系统坑惨了,一整天都在输。和心爱抱怨了一番之后,聪明的她想出了下面这个题。

题目

可爱的智乃会给你她某一天的游戏记录,这一天她打了 \(n\) 局游戏,赢了 \(B\) 场,当然具体的输赢记得不是很清楚了,她想知道根据这天的游戏记录,她能够达到的最高段位是什么呢?

智乃知道你可能不明白什么是段位系统,所以善良的她打算给你解释一下

  • 正常的段位系统有 \(N\) 个段,称第 \(i\) 个段为段位 \(i\)。
  • 每个段位都有 \(m\) 颗星,每赢一局得一颗星,输一局掉一颗星。
  • 如果在段位 \(i\) 已经拥有 \(m\) 颗星,且再赢一局,则进入段位 \(i+1\)。若在当前段位已经拥有 \(0\) 颗星,且再输一局,则进入段位 \(i−1\)。
  • 从一个段位进入下一个段位会清空之前段位的星,并获得 \(1\) 颗星。从一个段位到达上一个段位会清空之前段位的星,获得 \(m−1\) 颗星。如果当前段位已拥有 \(1\) 颗星,且再输一局,则当前段位拥有 \(0\) 颗星。
  • 升星机制:每连赢 \(d\) 局之后会 \(+1\) 颗星,无上限。如果连赢 \(kd\) 局则会 \(+k\) 颗星。
  • 玩家初始时从段位 \(1\) 的 \(1\) 颗星开始。
  • 完成 \(n\) 局游戏后段位绝对不可能是 \(0\) 或者负整数,但中途可以出现此情况,我们认为这是游戏的一种结算机制。若最终段位为 \(0\) 或负整数,视为段位 \(1\)。

(以上规则基本与某多人社交游戏相同)

为了让问题更简单也更符合她的游戏体验,她告诉你 \(m=5\)。

她会给你一个长度为 \(n\) 的字符串 \(S=⋯\) 表示当天的游戏记录,其中 \(Si∈0,1,?\),\(0/1\) 表示对局的输赢,\(?\) 表示她不记得对局情况了。她还会给你 \(d,B\) 两个自然数,分别表示升星需连续赢的对局数和总共赢的对局数。

输入格式

第一行两个正整数 \(d,B\)。

第二行一个长度为 \(n\) 的字符串,表示对局情况。

输出格式

一行一个正整数,表示根据智乃给出的对局记录所能够到达的最高段位。

数据范围

对于 \(100\%\) 的数据,\(n≤5000,B≤5000,d≤200\)。

测试点 \(d=?\) \(B=?\) \(n=?\)
\(1\) \(3\) \(60\) \(100\)
\(2\) \(5\) \(300\) \(500\)
\(3\) \(30\) \(550\) \(1000\)
\(4\) \(55\) \(3000\) \(5000\)
\(5\) \(55\) \(4000\) \(5000\)
\(6\) \(10\) \(4000\) \(5000\)
\(7\) \(8\) \(3041\) \(3500\)
\(8\) \(3\) \(3156\) \(5000\)
\(9\) \(150\) \(4000\) \(5000\)
\(10\) \(150\) \(4000\) \(5000\)

格式说明

输出时每行末尾的多余空格,不影响答案正确性

样例输入1

5 5
1111100

样例输出1

2

样例输入2

5 5
0011111

样例输出2

1

样例解释2

经过 \(7\) 次比赛后的段位和星数分别为:

  1. 段位 \(1\),星数 \(0\);
  2. 段位 \(−1\),星数 \(4\);
  3. 段位 \(−1\),星数 \(5\);
  4. 段位 \(1\),星数 \(1\);
  5. 段位 \(1\),星数 \(2\);
  6. 段位 \(1\),星数 \(3\);
  7. 胜利加一颗星,连续胜利 \(5\) 场再加一颗星,最终段位 \(1\),星数 \(5\)。

因此最高的最终段位为 \(1\)。

样例输入3

5 20
100???01110000????0101??1000?????11110000101??100

样例输出3

1

Solution

这个题目有几个地方比较巧妙,不是很容易能想得到

初次做的时候很容易想到设计状态 \(dp_{i,j,k}\) 表示前 \(i\) 局,赢了 \(j\) 局,当前连胜 \(k\) 局的最高分数(把段位和星数结合考虑处理成分数,最后算答案的时候把分数重新换算成段位),转移也很好转移(这里不再展示),但是显然会 \(TLE\),而且数组也开不下

首先将段位+星数转化为分数:1段0星即以下->0分,1段1星->1分,……

这里有个比较巧妙的地方——我们不要1段5星->5分,2段0星->6分,2段1星->7分,最好是1段5星(2段0星)->5分,2段1星->6分,这样更方便计算。但是问题是,这样对应怎么区分满星和0星(它们的分数都是 \(m\) 的倍数,即 \(5\) 的倍数)?

仔细思考一下,其实满星和0星是等效的,只不过如果最后一局赢了并且分数是 \(m\) 的倍数,说明是从 \(m-1\) 颗星升到的 \(m\) 星;最后一局输了并且分数是 \(m\) 的倍数,说明是从 \(1\) 星降到了 \(0\) 星。也就是说只需要看最后一局是输是赢,即可顺利区分满星和0星

这两种不同情况统一之后,问题就变得好思考一些了,不用管升段还是降段,只计算分数就好。

总共赢了 \(B\) 局,输了 \(n-B\) 局,初始分数是 \(1\) 分(1段1星),那么最终的分数就是 \(max\{1+B-(n-B)+最多连胜奖励, 0\}\)

问题转化为,给定一堆 \(?\) 可以选择 \(0/1\) , 要求连续的 \(1\) 获得连胜奖励最多。

设第 \(i\) 段连续的 \(1\) 的长度是 \(len1_i\) ,要求的就是 \(max\{\Sigma_i{\lfloor \frac{len1_i}{d} \rfloor} \}\)

设 \(dp_{i,j,0/1}\) 表示前 \(i\) 局,赢了 \(j\) 局,当前这一局赢(\(1\))或者输(\(0\)),所获得的的连胜奖励

这一局输了的转移方程很好写:\(dp_{i,j,0}=max\{dp_{i-1,j,0},dp_{i-1,j,1}\}\),肯定没有连胜,直接继承上一局的连胜奖励情况

这一局赢了如何转移呢?

比如以下这种情况:\(d=3\),局面是\(1111111\),显然这种情况会获得2分奖励。

我其实可以理解成:前 \(6\) 局,有两个 \(3\) 局连胜,获得 \(2\) 分奖励;

也可以理解成:后 \(6\) 局,有两个 \(3\) 局连胜,获得 \(2\) 分奖励;

甚至还可以理解成:前 \(3\) 局连胜,后 \(3\) 局连胜,获得 \(2\) 分奖励。

以上几种理解其实是等效的。换句话来说,只要保证你视为连胜的场次不相交,最终算出来的答案是一样的。

那么这就为我们赢了的情况提供了转移的思路

\[dp_{i,j,1}=max \begin{cases} max\{dp_{i-1,j-1,1},dp_{i-1,j-1,0}\}& 直接继承上一局的连胜情况\\ max\{dp_{i-d,j-d,1},dp_{i-d,j-d,0}\}+1& 视为当前的d局连胜(如果可以连胜d局),获得1分奖励 \end{cases}\]

最终的分数就是 \(max\{1+B-(n-B)+max\{dp_{n,B,0},dp_{n,B,1}\}, 0\}\)

最后把分数转化为段位,每 \(5\) 分是 \(1\) 段,不过如果分数是5的整数倍,就要看最后一局选的是赢还是输,根据之前说的判断方法就可以了

时间复杂度 \(O(nB)\),完美通过此题

至此这道题目就解决了。

附上代码

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define N (5000+5)
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
int d,B,n,m=5;
char s[N];
int dp[N][N][2];
bool may[N];			//may[]:当前位置有没有可能连胜了d局
int main(){
	scanf("%d%d",&d,&B);
	scanf("%s",s+1);
	n=strlen(s+1);
	memset(dp,-0x3f,sizeof(dp));
	dp[0][0][0]=dp[0][0][1]=0;
	int cnt=0;
	for(int i=1;i<=n;i++){
		if(s[i]=='0') cnt=0;
		else{
			cnt++;
			if(cnt>=d){
				may[i]=1;
			}
		}
	}
	for(int i=1;i<=n;i++){
		int minj=min(i,B);
		for(int j=0;j<=minj;j++){
			if(s[i]=='0'){
				dp[i][j][0]=max(dp[i][j][0],max(dp[i-1][j][0],dp[i-1][j][1]));
			}
			else if(s[i]=='1'){
				if(j>=1) dp[i][j][1]=max(dp[i][j][1],max(dp[i-1][j-1][0],dp[i-1][j-1][1]));
				if(j>=d&&i>=d&&may[i]) dp[i][j][1]=max(dp[i][j][1],max(dp[i-d][j-d][0],dp[i-d][j-d][1])+1);
			}
			else{
				dp[i][j][0]=max(dp[i][j][0],max(dp[i-1][j][0],dp[i-1][j][1]));
				if(j>=1) dp[i][j][1]=max(dp[i][j][1],max(dp[i-1][j-1][0],dp[i-1][j-1][1]));
				if(j>=d&&i>=d&&may[i]) dp[i][j][1]=max(dp[i][j][1],max(dp[i-d][j-d][0],dp[i-d][j-d][1])+1);
			}
		}
	}
	int delta=0;			//delta: 处理分数是m的倍数的情况
	int maxx=max(dp[n][B][0],dp[n][B][1]);
	int ans=max((B<<1)-n+maxx+1,0);
	if(dp[n][B][0]<dp[n][B][1]&&ans%m==0) delta=-1;
	ans=ans/m+1+delta;
	if(ans==0) ans=1;
	printf("%d\n",ans);
	return 0;
}

标签:一局,连胜,期实训,题解,样例,段位,max,dp
From: https://www.cnblogs.com/truman2022/p/17565940.html

相关文章

  • CF786E ALT 题解
    为什么你们第一眼都能想到最小割,我第一眼都只能想到费用流。为什么你们的做法都这么短,我一写就是\(5KB\)。费用流有一个基本矛盾,就是守卫只需拥有一只狗和每一个人都需要守卫有狗的基本矛盾。由于需求与供给不平衡,所以流量不好确定。如果有人费用流过了来长沙火车站,疯狂星期四......
  • [SDOI2010] 代码拍卖会 题解
    [SDOI2010]代码拍卖会题解题目描述一个\(n,n\le10^{18}\)位数,仅由\(1\sim9\)组成,后一位数字一定大于等于前一位数字。求这些数中可以被\(m,m\le500\)整除的有多少,对\(999911659\)取模。解析这个数一定形如\(112334455677788999\)可以把它拆成\[\begin{aligned}......
  • 黑群晖NAS7.0+安装问题解决经验分享
    感谢网上各种帖子及分享,为大家提供一个解决思路,机器配置多种多样,解决办法也仅供参考;1、引导后,无法找到群晖  遇到无法找到群晖的情况,首先要排除引导不兼容的问题。在bios中分别设置传统引导模式和UEFI引导模式尝试启动试下。最新版7.0.1的引导文件是两种启动方式都支持的,理......
  • 题解 [USACO18JAN] MooTube G
    题目链接可以发现,对于一个固定的\(k\),所有边权小于\(k\)的边对答案是没有贡献的,因为一条边的相关性是最小相关性,这也意味着我们不能从\(<k\)的边到达其他的点。由于本题有多组询问,所以对于没有用的边,将其看做被删除,有用的边,将其看做被插入。考虑到本题实际上要维护连通块......
  • 洛谷 P1122 最大子树和 题解
    一道入门的树形DP。首先我们对于数据进行有序化处理,这便于我们利用数据结构特点(可排序性)来发觉数据性质(有序、单调、子问题等等性质),以便于后续的转化、推理和处理。有序化可以“转化和创造”性质首先将视角从无根树切换为有根树,这样我们就可以得到一个带有最优子结构、无后效性......
  • HDU 5492 Find a path 题解
    Description在矩阵中,找一条到从\((1,1)\)到\((n,m)\)(只能向上,右走)的路径,使路径上方差最小。输出方差平方乘\(n+m-1\)的结果。对于所有数据,\(1\leqn,m,A_{i,j}\leq30\)。Solution设路径上的数为\(A_{1},A_{2},A_{3},\cdots,A_{n+m-1}\),\(\overline{A}\)为其平均数,则答......
  • 题解 序列合并
    题目链接首先不难想到,最小数的一定是\(a_1+b_1\),次小的数是\(a_1+b_2\)和\(a_2+b_1\)中小的。得出结论,若\(a_i+b_j\)是第\(k\)小,那么\(a_{i+1}+b_j\)和\(a_i+b_{j+1}\)有可能成为第\(k+1\)小。这是一个很优秀的性质,这意味着我们可以通过最小值推出次小值,再通过......
  • “范式杯”2023牛客暑期多校训练营1 蒻蒟题解
    A.AlmostCorrect题意:给定一个01串,要求构造一系列排序操作(xi,yi),使得经过这些排序操作后可以使与给定01串等长的所有其他01串完全排好序,而给定的01串无法完全排好序Solution构造题我们考虑到对0和1的位置进行统计,统计得出最左边的1的位置为l,最右边的0的位置为r我们进行三次......
  • 【SP21463 题解】
    Descirption给定\(n\timesm\)的矩阵,求最大子矩阵的面积满足每一行每一列都构成等差数列。Solution我们另\(up_{i,j}\)表示最小的\(k\)满足\((i,k),(i,k+1),\cdots,(i,j)\)构成等差数列,可以用\(\mathcalO(nm)\)求出。对于一个矩阵的左上角\((a,b)\),右下......
  • BZOJ 1461 题解
    考虑设计一个哈希函数\(hash(x)=f(x)\timesbase^x\)。其中\(f(x)\)表示\(\sum_{j=1}^{i-1}[j<i]\)。然后类似于滑动窗口计算区间哈希值,加入一个数就计算贡献,减去一个数就计算这个数产生了贡献,两个东西都可以树状数组维护,那么愉快做完了。#include<bits/stdc++.h>#de......