1 条题解
-
0
Preface
😕 👀️
(NaHCO3:
我们要严厉表扬这种使用AI写标程并且使用标程生成代码并且不做对拍的行为。) 我在此向所有同学道歉,出了道错题……
(其实这题不看样例还是可以做的)题意
Tap能使你收获 a 的连击数,但是会耗费你 A 的精力和一根手指。
Hold能使你收获 b 的连击数,但是会耗费你 B 的精力和一根手指。
Flick能使你收获 c 的连击数,但是会耗费你 C 的精力和一根手指。
Drag能使你收获 d 的连击数,但是会耗费你 D 的精力和一根手指。
由于小Q同时最多只能使用4根手指,并且他的精力有限,只有 w 。一旦他的精力耗尽,他就会「Track Lost」,并且骂骂咧咧地退出游戏。
小Q想要知道,在不耗光他的精力的前提下,他最多能获得多少「连击」数和在获得最多连击数的情况下最多能完全抵挡多少次攻击(攻击定义见输入)。由于答案可能很大,请你对 取模。 (请注意,一旦小Q没有接到某次攻击的任何一个道具,「连击」数就会清零)
思路
考虑使用动态规划,实际上是状压dp
(因为这题长得很像多重背包!)
-
dp[i][j]
表示处理完第i
波攻击后,剩余精力为j
时的最大连击数。 -
对于每一波攻击,先计算当前波中每个道具的实际精力消耗
cur_w
和连击数cur_v
。 -
枚举使用手指的数量
use
(最多 4 个),使用位运算生成使用use
个道具的所有组合(通过mask
,见代码)。 -
计算每种组合的精力消耗
cur_w_sum
和连击数cur_v_sum
,并更新dp
数组。 -
计算最多能抵挡的攻击次数:
- 从最后一波开始向前遍历,同样枚举使用手指的数量和道具组合,找到能让当前
dp
值等于上一波dp
值加上当前组合连击数的情况,更新能量并增加抵挡攻击次数cur_attack
。
- 从最后一波开始向前遍历,同样枚举使用手指的数量和道具组合,找到能让当前
时间复杂度
- 动态规划部分:
- 外层循环遍历
m
波攻击,对于每一波攻击,内层循环枚举使用手指的数量,最多为 4 次,然后生成道具组合的时间复杂度是 O(),计算每种组合的属性的时间复杂度是O(k) ,更新dp
数组的时间复杂度是 O()。
- 外层循环遍历
- 计算最多能抵挡的攻击次数部分:
- 从最后一波开始向前遍历,时间复杂度为O(m) ,对于每一波攻击,枚举使用手指的数量和道具组合的时间复杂度与动态规划部分相似,约为O() 。
- 总的时间复杂度为 O()。
标程
#include<bits/stdc++.h> #define int long long #define inf 1e18 #define mod 1000000007 using namespace std; int read() { char c=getchar(); int f=1,k=0; while(c<'0'||c>'9') { if(c=='-')f=-1; c=getchar(); } while(c>='0'&&c<='9') { k*=10; k+=c-'0'; c=getchar(); } return k*f; } string readStr() { string s = ""; char c = getchar(); while(c == ' ' || c == '\n') c = getchar(); while(c!= ' ' && c!= '\n') { s += c; c = getchar(); } return s; } /* readStr : * 功能:从标准输入读取一个字符串, 跳过空格和换行符。 * 实现:使用 `getchar` 函数逐字符读取, 拼接字符直到遇到空格或换行符。*/ void write(int k) { if(k<0) { putchar('-'); k=-k; } if(k<10) putchar(k+'0'); else write(k/10),putchar(k%10+'0'); return; } int n,m,k,ww; int a[1000005]; int v[1000005],w[1000005]; int ans1,ans2; signed main() { freopen("phigros.in","r",stdin); freopen("phigros.out","w",stdout); n = read(),m = read(),k = read(),ww = read(); int cnt = 0; for(int i=1;i<=m;i++) { for(int j=1;j<=k;j++) { string str; str = readStr(); cnt ++; if(str == "Tap")a[cnt] = 1; if(str == "Hold")a[cnt] = 2; if(str == "Flick")a[cnt] = 3; if(str == "Drag")a[cnt] = 4; //这里将字符串转化为数字,便于后续操作 } } for(int i=1;i<=4;i++) { v[i] = read(); } for(int i=1;i<=4;i++) { w[i] = read(); } vector<vector<int> > dp(m + 1, vector<int>(ww + 1, 0)); for(int i=1;i<=m;i++) { vector<int> cur_w(k); vector<int> cur_v(k); for(int j=0;j<k;j++) { cur_w[j] = w[a[(i - 1) * k + j + 1]]; cur_v[j] = v[a[(i - 1) * k + j + 1]]; /* 对于每一波攻击,先计算当前波中每个道具的实际精力消耗 `cur_w` 和连击数 `cur_v`。*/ } // 枚举当前波使用的手指数量 for (int use = 0; use <= min((long long)4, k); use++) { /* 枚举使用手指的数量 `use`(最多 4 个),使用位运算生成使用 `use` 个道具的所有组合(通过 `mask`,见代码)。*/ vector<int> cur_index; /*对于每一波攻击,枚举使用手指的数量 use(最多为 4 或 k,取较小值)。 然后,使用状态压缩 mask 来表示道具的选择情况, (1 << k) 表示有 k 个道具时可能的不同组合状态, 总共有2^k种组合。对于每种组合, __builtin_popcount(mask) 函数计算 mask 中 1 的数量, 当 1 的数量等于 use 时, 表示使用 use 个道具的一种组合, 将该 mask 存储在 cur_index 中。 总结来说,cur_index 存储的是当前波次攻击中, 使用 use 个道具的所有可能组合。 这样,后续代码可以根据存储在 cur_index 中的mask来计算不同道具组合下的精力消耗和连击数, 并更新dp。*/ for (int mask = 0; mask < (1 << k); mask++) { if (__builtin_popcount(mask) == use) {/*__builtin_popcount()函数计算的是 mask的二进制表示中有多少个1, 在这里就是枚举每一个道具选或不选 的总方案数*/ cur_index.push_back(mask); } } for (int mask : cur_index) { int cur_w_sum = 0; int cur_v_sum = 0; //* 计算每种组合的精力消耗 `cur_w_sum` 和连击数 `cur_v_sum`,并更新 `dp` 数组。 for (int j = 0; j < k; j++) { if ((mask >> j) & 1) { cur_w_sum += cur_w[j]; cur_v_sum += cur_v[j]; } } for (int j = 0; j <= ww; j++) { if (j >= cur_w_sum) { dp[i][j] = max(dp[i][j], dp[i - 1][j - cur_w_sum] + cur_v_sum); /** `dp[i][j]` 表示处理完第 `i` 波攻击后,剩余精力为 `j` 时的最大连击数。*/ } } } } } ans1 = 0; for(int j=0;j<=ww;j++) { ans1 = max(ans1, dp[m][j]); } ans1 %= mod; write(ans1); putchar(' '); int cur_energy = ww; int cur_attack = 0; for (int i = m; i >= 1; i--) {/* 计算最多能抵挡的攻击次数: * 从最后一波开始向前遍历, 同样枚举使用手指的数量和道具组合, 找到能让当前 `dp` 值等于上一波 `dp` 值 加上当前组合连击数的情况, 更新精力并增加抵挡攻击次数 `cur_attack`。*/ for (int use = 0; use <= min((long long)4, k); use++) { vector<int> cur_index; for (int mask = 0; mask < (1 << k); mask++) { if (__builtin_popcount(mask) == use) { cur_index.push_back(mask); } } for (int mask : cur_index) { int cur_w_sum = 0; int cur_v_sum = 0; for (int j = 0; j < k; j++) { if ((mask >> j) & 1) { cur_w_sum += w[a[(i - 1) * k + j + 1]]; cur_v_sum += v[a[(i - 1) * k + j + 1]]; } } if (cur_energy >= cur_w_sum && dp[i][cur_energy] == dp[i - 1][cur_energy - cur_w_sum] + cur_v_sum) { cur_energy -= cur_w_sum; cur_attack++; break; } } } } write(cur_attack); return 0; }
-
信息
- ID
- 1068
- 时间
- 3800ms
- 内存
- 256MiB
- 难度
- 9
- 标签
- (无)
- 递交数
- 66
- 已通过
- 3
- 上传者