“葡萄城杯”牛客周赛 Round 53
A 小红小紫投硬币
链接:https://ac.nowcoder.com/acm/contest/86387/A
来源:牛客网
题目描述
小红和小紫玩投硬币游戏,小紫投了 𝑛 枚硬币,小红投了 𝑛+1 枚硬币。硬币都是质地均匀的、且每次投掷正面反面的概率相等均为 1/2 。
小红想知道,她投的硬币正面朝上的次数比小紫投的硬币正面朝上的次数多的概率是多少。
输入描述:
第一行输入一个整数 𝑛 (1≤𝑛≤2×105)表示小紫投的硬币数。
输出描述:
输出一个实数,表示小红投的硬币正面朝上的次数比小紫投的硬币正面朝上的次数多的概率,为了减少计算过程中误差带来的影响,当你的输出与标准答案的误差不超过 10−6 时,均被视为正确。
具体的说,使用 𝑎 代表你的答案,使用 𝑏 代表标准答案,当
∣
a
−
b
∣
m
a
x
(
1
,
b
)
≤
1
0
−
6
\frac{|a-b|}{max(1,b)}{}\leq 10^{-6}
max(1,b)∣a−b∣≤10−6 成立时, 𝑎 被视为正确答案。
示例1
输入
2
输出
0.500000
说明
小红的投掷结果有 8 种情况:{正,正,正},{正,正,反},{正,反,正},{反,正,正},{正,反,反},{反,正,反},{反,反,正},{反,反,反}。
小紫的投掷结果有 4 种情况:{正,正},{正,反},{反,正},{反,反}。
一共有 32 种情况,其中小红投的硬币正面朝上的次数比小紫投的硬币正面朝上的次数多的情况有 16 种,所以概率是 1/2 。
题解
所以这玩意老唬人了,当然我数学不好,只能借用别人写好的。。。。。
#include<bits/stdc++.h>
using namespace std;
int n;
int main(){
int i,j,k;
printf("0.500000\n");
return 0;
}
B 小红的字符串
链接:https://ac.nowcoder.com/acm/contest/86387/B
来源:牛客网
题目描述
小红有一个长度为 𝑛 的字符串 𝑠 ,她可以执行以下操作:
● 选择一个索引 𝑖 ,将 𝑠𝑖 的字母右移替换为字母表中的下一个字母。例如:‘a’ 右移一位为 ‘b’ ,特别的,‘z’ 右移一位为 ‘a’ 。
现在请你帮助小红求出把 𝑠 变成回文串的最少操作次数。
一个字符串被称作回文串当且仅当这个字符串从左往右读和从右往左读都是相同的,例如 “level” 、“reder” 都是回文串。
输入描述:
在一行上输入一个长度不超过1000 且仅由小写字母构成的字符串字符串 𝑠 。
输出描述:
在一行上输出一个整数,代表把 𝑠 变成回文串的最少操作次数。
示例1
输入
abcd
输出
4
说明
其中一个可行操作的方案是 “dccd” 。
示例2
输入
nuhhhh
输出
19
题解
第二题我感觉是最简单的。思路很简单,对称的位置要求相同嘛,所以你固定一个另外一个移动。26个字母对吧,如多正向移动>13了,就证明逆向要小的对吧。
上代码吧
#include<bits/stdc++.h>
using namespace std;
string s;int ans;
int main(){
int i,j,k;
cin>>s;
for(i=0;i<s.length()/2;i++){
int d=abs(s[i]-s[s.length()-i-1]);
if(d>13)d=26-d;
ans+=d;
}
cout<<ans<<endl;
return 0;
}
C 小红的 01 消除
链接:https://ac.nowcoder.com/acm/contest/86387/C
来源:牛客网
题目描述
小红有一个长度为 𝑛 且仅由 ‘0’ 和 ‘1’ 构成的字符串 𝑠,她有以下三种操作:
● 删除任意一个子串 “11”,例如 “01101” 操作之后变成 “001”;
● 删除任意一个子串 “01”,例如 “01101” 操作之后变成 “101” 或者 “011”;
● 删除任意一个子串 “00”,例如 “00100” 操作之后变成 “100” 或者 “001” 。
小红至多可以执行操作一 𝑥 次、操作二 𝑦 次、操作三 𝑧 次,问小红最多可以执行多少次操作二。
输入描述:
第一行输入一个整数 𝑛(1≤𝑛≤105) 代表字符串的长度 。
第二行输入一个长度为 𝑛 且仅由 ‘0’ 和 ‘1’ 构成的字符串 𝑠 。
第三行输入三个整数 𝑥,𝑦,𝑧 (0≤𝑥,𝑦,𝑧≤105) 代表各个操作至多执行的次数。
输出描述:
在一行上输出一个整数,代表小红最多可以执行多少次操作二。
示例1
输入
7
1110011
3 3 3
输出
2
说明
先执行一次操作一,得到 “10011”;再执行一次操作二,得到 “101”;再执行一次操作二,得到 “1”。
最多可以执行两次操作二。
示例2
输入
6
110010
0 1 0
输出
1
题解
比较考验智商的一道题
你看他问题,只问你最多能删除多少个01
所以你可以大胆假设和11 以及00没有任何关系
实际就是没有任何关系
思路就是前面有多少个0,后面如果有相对应的1,跟括号问题很像
#include<bits/stdc++.h>
using namespace std;
int n,x,y,z,sum,ans;
int main(){
int i,j,k;
cin>>n;
for(i=1;i<=n;i++){
int now;scanf("%1d",&now);
if(now==0)sum++;
else {
if(sum>0){
sum--;
ans++;
}
}
}
cin>>x>>y>>z;
cout<<min(y,ans)<<endl;
return 0;
}
D 小红组比赛
链接:https://ac.nowcoder.com/acm/contest/86387/D
来源:牛客网
题目描述
小红希望出一场题目,但是他的实力又不够,所以他想到可以从以前的比赛中各抽一题,来组成一场比赛。不过一场比赛的难度应该是有限制的,所以所以这一场比赛会给一个目标难度分数 target 。
小红选 𝑛 场比赛,每场 𝑚 个题,小红会从每一场选一道题,使其组成的题的难度分数尽量接近 target 。小红想知道挑选的题的难度分数与 target 相差的最小值是多少。
输入描述:
第一行输入两个整数 𝑛,𝑚 (1≤𝑛≤100,1≤𝑚≤20) 代表小红选了 𝑛 场比赛,每场比赛有 𝑚 道题。
此后 𝑛 行,第 𝑖 行输入 𝑚 个整数 𝑎𝑖,𝑗 (1≤𝑎𝑖,𝑗≤50) 代表第 𝑖 场比赛的第 𝑗 道题的难度分数。
最后一行输入一个整数 target (1≤target≤5000) 代表小红希望这一场比赛的难度分数。
输出描述:
在一行上输出一个整数,表示挑选的题的难度分数与 target 相差的最小值。
示例1
输入
3 3
1 4 7
2 5 8
3 6 9
10
输出
1
说明
小红可以选第一场比赛的第一道题,第二场比赛的第一道题,第三场比赛的第二道题,这样挑选的题的难度分数为 1+2+6=9,与 target 相差的最小值为 1。
题解
额,dp求解,就是看提交的时候,看到一堆下面这类代码
#include<bits/stdc++.h>
using namespace std;
const int N = 5010;
char s[N];
int ma[N];
int main () {
int n, m, t;
cin >> n >> m;
for(int i = 1; i <= n; i ++) {
for(int j = 1; j <= m; j ++) {
int a;
cin >> a;
ma[i] = max(ma[i], a);
}
}
cin >> t;
int sum = 0;
for(int i = 1; i <= n; i ++) sum += ma[i];
cout << abs(t - sum);
return 0;
}
这玩意明显不对啊!!!怎么可能每次考试取最大值啊?还是数据太弱了??
这个问题可以通过动态规划(DP)来解决,但直接应用传统的DP方法可能不够高效,因为状态空间会非常大(每场比赛选择一个题目,总共有 n×m 种选择方式)。不过,考虑到 m 的范围较小(1≤m≤20),我们可以采用一种类似“分组背包”的DP方法。
我们可以将每场比赛看作一个“组”,每个组内有 m 个物品(即题目),每个物品的价值和重量都是其难度分数。我们的目标是从这些组中挑选出物品,使得总价值(总难度分数)与目标值 target 相差最小。
这里的关键是,对于每组内的物品,我们不需要枚举所有可能的组合来得到所有可能的和(这会导致时间复杂度爆炸),而是可以预先计算出每个组能形成的所有可能的和,并记录下这些和对应的最小差值(与目标值的差值)。
使用类似01背包的DP方法,但这次是对预处理后的“组”进行操作,而不是单个物品。
定义 dp[i] 为达到难度分数 i 时与 target 的最小差值。初始时,除了 dp[0]=0 外,其他所有 dp[i] 都设为无穷大。
遍历每个组,对于该组的每个可达和 sum,更新 dp[j](其中 j 是当前可达的总难度分数),即 dp[j]=min(dp[j],dp[j−sum]+∣j−target∣)(但注意这里我们只关心差值,所以实际更新时可能会有些不同,具体取决于你如何记录差值)。
#include<bits/stdc++.h>
using namespace std;
const int N=110;
int n,m,a[105][25],f[N][5010];
int tag;
int main()
{
int i,j,k;
cin>>n>>m;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
cin>>a[i][j];
cin>>tag;
f[0][0]=1;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
for(k=a[i][j];k<5000;k++)
f[i][k]+=f[i-1][k-a[i][j]];
int ans=1e9;
for(i=1;i<=5000;i++)
if(f[n][i]) ans=min(ans,abs(tag-i));
cout<<ans<<endl;
return 0;
}
E 折半丢弃
链接:https://ac.nowcoder.com/acm/contest/86387/E
来源:牛客网
题目描述
已知长度为 𝑛 的序列 𝑎1,𝑎2,…,𝑎𝑛,定义一次操作的过程为:选择任意一个元素,随后,将
⌊𝑎𝑖 / 2⌋(向下取整)添加到原序列的结尾,并将 𝑎𝑖 从原序列中删除。
你可以进行任意多次操作(也可以一次操作都不做),要求使得序列的 MEX 最大。
数组的MEX定义为:没有出现在数组中的最小非负整数,例如,数组 {3,1,2} 的 MEX 为
0 。
输入描述:
每个测试文件均包含多个测试点。第一行输入一个整数 𝑇 (1≤𝑇≤104) 代表测试数据组数,每组测试数据描述如下:
第一行输入一个整数 𝑛 (1≤𝑛≤105),代表序列的长度。
第二行输入 𝑛 个整数 𝑎1,𝑎2,…,𝑎𝑛 (0≤𝑎𝑖≤106) 。数字彼此间通过空格间隔。
除此之外,保证所有的 𝑛 之和不超过 105 。
输出描述:
对于每一个测试点,在一行上输出一个整数,代表当前序列的最大 MEX 。
示例1
输入
4
6
0 1 2 4 8 12
5
1 2 4 8 12
4
0 1 3 4
10
1 1 1 2 1 3 2 2 1 1
输出
5
5
4
4
说明
对于第一个测试点:
● 操作第 6 个元素 12 ,随后将 ⌊12 / 2⌋=6 加入到序列结尾,新的序列为 {0,1,2,4,8,6} ;
● 继续操作刚刚新加入的元素 6 ,随后将 ⌊6 / 2⌋=3 加入到序列结尾,新的序列为 {0,1,2,4,8,3};
此时,得到原序列的最大 MEX 为 5 。可以穷举证明此时没有比 5 更大的答案。
对于第二个测试点:
● 操作第 5 个元素,新的序列为 {1,2,4,8,6} ;
● 继续操作第 5 个元素,新的序列为 {1,2,4,8,3} ;
● 选择第 4 个元素操作,新的序列为 {1,2,4,3,4} ;
● 选择第 3 个元素操作,新的序列为 {1,2,3,4,2} ;
● 选择第 2 个元素操作,新的序列为 {1,3,4,2,1} ;
● 最后选择第 1 个元素,新的序列为 {3,4,2,1,0} ;
此时,得到答案 MEX 为 5。
题解
想偏了,一开始想到的是对于每个数/2可以到多少存在数组里,然后放在一起排序并做好标记,保证每个调整后的数只取一次,最终得到结果。结果发现这个完全不显示,数据范围大,而且产生的重复数字非常多,根本没有办法排列。
正解是二分+贪心。
最小值最大/最大值最小的问题,很明显有一个临界点。
然后贪心从大到小贪心,一步一步的凑数字,看有没有能够达到当前数字的数。
最后!!!!一定要清空数据!!!!!多组数据一定要清空脏数据!!!!!
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int t, n, a[N], cal[N], f[N];
bool check(int x) {
int i,j,k;
for (i = 0; i <= n; i++)
f[i] = cal[i];
for (i = n - 1; i >= 0; i--) {
if (i < x) {
if (f[i] == 0)
return false;
f[i]--;
}
f[i / 2] += f[i];
}
return true;
}
int main() {
int i,j,k;
cin>>t;
while(t--){
memset(cal, 0, sizeof cal);
cin>>n;
for (i = 1; i <= n; i++) {
cin>>a[i];
while (a[i] && a[i] >= n)
a[i] /= 2;
cal[a[i]]++;
}
int l = 0, r = n, mid, res = 0;
while (l <= r) {
mid = (l + r) >> 1;
if (check(mid)) {
res = mid;
l = mid + 1;
} else r = mid - 1;
}
printf("%d\n",res);
}
return 0;
}
F 小红走矩阵
链接:https://ac.nowcoder.com/acm/contest/86387/F
来源:牛客网
题目描述
𝑛×𝑚 的矩阵由障碍和空地组成,初始时小红位于起点 (1,1) ,她想要前往终点 (𝑛,𝑚) 。小红每一步可以往上下左右四个方向的空地移动一格。
小红在起点处可以进行最多一次操作:选择矩阵中的一处障碍替换为空地,但代价是小红必须选择失去向上下左右四个方向中一个移动的能力。
求小红从起点到达终点的最小步数,如果无法到达则输出 −1 。
输入描述:
第一行输入两个整数 𝑛,𝑚 (1≤𝑛,𝑚≤1000) 代表矩阵的大小。
此后 𝑛 行,每行输入 𝑚 个字符 𝑎1𝑎2…𝑎𝑛(𝑎𝑖∈{‘X’,‘.’}) 描述矩阵中这一行的情况,其中 ‘X’(Ascii:88)代表障碍,‘.’(Ascii:46)代表空地。保证起点和终点都是空地。
输出描述:
在一行上输出一个整数,表示小红从起点到达终点的最小步数;如果无论怎么操作都无法到达,则直接输出 −1 。
示例1
输入
4 4
…X.
XXX.
.X…
.X…
输出
6
说明
小红失去向上走的能力,消除 (1,3) 处障碍,从起点到终点的最小步数为 6 。
示例2
输入
4 4
.XX.
XXX.
.X…
.X…
输出
-1
说明
小红最多只能删除一个障碍,无法到达终点。
题解
不会,然后他们说的分层bfs,敲,根本没听过
https://ac.nowcoder.com/acm/discuss/blogs?tagId=270546
h, w = list(map(int, input().split()))
g = []
for _ in range(h):
g.append(input())
from math import inf
dp = [[[[inf] * w for _ in range(h)] for _ in range(2)] for _ in range(5)]
from collections import deque
deq = deque()
if g[0][0] == '.':
deq.append((4, 0, 0, 0))
dp[4][0][0][0] = 0
for i in range(4):
dp[i][0][0][0] = 0
deq.append((i, 0, 0, 0))
else:
for i in range(4):
dp[i][1][0][0] = 0
deq.append((i, 1, 0, 0))
while len(deq) > 0:
op, d, y, x = deq.popleft()
cost = dp[op][d][y][x]
for (i, (dy, dx)) in enumerate([(-1, 0), (1, 0), (0, -1), (0, 1)]):
ty, tx = y + dy, x + dx
if 0 <= ty < h and 0 <= tx < w:
if g[ty][tx] == '.':
if dp[op][d][ty][tx] > cost + 1:
dp[op][d][ty][tx] = cost + 1
deq.append((op, d, ty, tx))
elif g[ty][tx] == 'X' and op != 4:
if d == 0 and op != i:
if dp[op][1][ty][tx] > cost + 1:
dp[op][1][ty][tx] = cost + 1
deq.append((op, 1, ty, tx))
res = inf
for i in range(5):
for j in range(2):
res = min(res, dp[i][j][-1][-1])
print (-1 if res == inf else res)
G 游游的删点直径
链接:https://ac.nowcoder.com/acm/contest/86387/G
来源:牛客网
题目描述
游游拿到了一棵树,她定义 𝑓(𝑖) 为:不经过 𝑖 号节点的所有路径中,最长的路径长度。特殊的,如果所有长度不小于1的路径都经过 𝑖 号节点,则 𝑓(𝑖)=0 。
现在游游希望你分别求出 𝑓(1) 到 𝑓(𝑛) 的全部的值。
输入描述:
第一行输入一个整数 𝑛 (1≤𝑛≤105) 代表树的节点数量。
此后 𝑛−1 行,第 𝑖 行输入两个整数 𝑢𝑖 和 𝑣𝑖 (1≤𝑢𝑖,𝑣𝑖≤𝑛; 𝑢𝑖≠𝑣𝑖) 表示树上第 𝑖 条边连接节点
𝑢𝑖 和 𝑣𝑖 。保证树联通,没有重边。
输出描述:
一共需要输出 𝑛 行,第 𝑖 行输出一个整数代表 𝑓(𝑖) 的值。
示例1
输入
3
1 2
2 3
输出
1
0
1
说明
不经过一号节点的最长路径为 2→3,长度为 1;
所有长度不小于 1 的路径都会经过二号节点,因此输出 0 ;
不经过三号节点的最长路径为 1→2,长度为 1。
示例2
输入
5
1 2
1 3
1 4
1 5
输出
0
2
2
2
2
题解
无,看大佬的题解去吧。。。。这玩意。。。。我看他们代码都费劲
总结
A题 数学题或者猜测题
B题 个人认为最简单
C题 完全是套着狼皮的🐏,想通了瞬间能写出来
D题 DP 模板题,额,该复习了
E题 二分+贪心
F题 。。。。。。。。。
难度个人感觉不小