学习 学习 训练小结 hymcr05 2024-09-22 2024-12-15 2024.7.11
序言
这次比赛只拿了 10 10 1 0 分,为什么呢?第三题打爆搜,原本可以拿 30 30 3 0 分,但是没打freopen
爆 0 0 0 了。第二题输出循序错了,也爆 0 0 0 ,只有第四题骗了 10 10 1 0 分。
作业调度方案
这是一道模拟,虽然是模拟,但是由于题目描述过于模糊(语文不好) ,所以没有读懂题目。
题意
给你一个 m m m , n n n 分别表示:机器数与工件数。
前 n n n 行依次表示每个工件的每个工序所使用的机器号,第 1 1 1 个数为第 1 1 1 个工序的机器号,第 2 2 2 个数为第 2 2 2 个工序机器号,等等。
最后让你求最小的加工时间。
解法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <bits/stdc++.h> using namespace std;int m, n;int gx[30 ] , last[30 ]; int a[42000 ], ans;bool mt[25 ][10050 ]; struct GG { int num, time; } s[220 ][220 ]; int main () {#ifdef ONLINE_JUDGE freopen ("jsp.in" , "r" , stdin); freopen ("jsp.out" , "w" , stdout); #endif cin >> m >> n; for (int i = 1 ; i <= m * n; i++) cin >> a[i]; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cin >> s[i][j].num; for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= m; j++) cin >> s[i][j].time; for (int i = 1 ; i <= n * m; i++) { int z = 0 , j = last[a[i]] + 1 ; gx[a[i]]++; int x = s[a[i]][gx[a[i]]].num; int v = s[a[i]][gx[a[i]]].time; while (z != j) { if (!mt[x][j]) z++; else z = 0 ; j++; } for (int k = j - v + 1 ; k <= j; k++) mt[x][k] = 1 ; last[a[i]] = j; ans = max (ans, last[a[i]]); } cout << ans; return 0 ; }
火柴棒等式
考场时并没有想到枚举数字的范围,发现时间复杂度非常高:O ( 1000 0 4 ) \mathcal {O(10000^4)} O ( 1 0 0 0 0 4 ) ,于是打表,但是打表时认为 n < 13 n < 13 n < 1 3 时答案为 0 0 0 ,再加上一些奇奇怪怪的 BUG \textrm {BUG} BUG ,就爆 0 0 0 了。事实上,我们的a a a ,b b b , c c c 只需要枚举到 1000 1000 1 0 0 0 即可,最后使用函数计算出 a a a ,b b b ,c c c 使用的火柴数量是否符合题意即可。
过于简单,就不贴代码了。
传纸条
题意
给出一个 n × m n \times m n × m 矩阵,要求从 ( 1 , 1 ) (1,1) ( 1 , 1 ) 走到 ( n , m ) (n,m) ( n , m ) 中经过的数值最大,再从 ( n , m ) (n,m) ( n , m ) 走到 ( 1 , 1 ) (1,1) ( 1 , 1 ) ,也是求数值最大的走法,但是走过的路不能重复。
解法
考场一眼就可以看出来是 DP \textrm {DP} DP ,但是有一个比较难处理的地方:走过的路不能重复走。
题目中的从 ( 1 , 1 ) (1,1) ( 1 , 1 ) 走到 ( n , m ) (n,m) ( n , m ) , 再从 ( n , m ) (n,m) ( n , m ) 走到 ( 1 , 1 ) (1,1) ( 1 , 1 ) ,我们就可以看作从 ( 1 , 1 ) (1,1) ( 1 , 1 ) 走到 ( n , m ) (n,m) ( n , m ) 走两遍。
于是设定状态:f i , j , k , l \large f_{i,j,k,l} f i , j , k , l 表示第一个纸条走到了 ( i , j ) (i,j) ( i , j ) ,第二个纸条走到了 ( k , l ) (k,l) ( k , l ) 。
转移方程:
f i , j , k , l = max i = 1 n max j = 1 n ( max k = 1 n ( max l = 1 n ( f i − 1 , j , k − 1 , l , f i , j − 1 , k − 1 , l , f i − 1 , j , k , l − 1 , f i , j − 1 , k , l − 1 ) ) )
\large{
f_{i,j,k,l} = \max_{i=1}^n\max_{j=1}^n(\max_{k=1}^n(\max_{l=1}^n(f_{i-1,j,k-1,l} , f_{i,j-1,k-1,l} , f_{i-1,j,k,l-1} , f_{i,j-1,k,l-1})))
}
f i , j , k , l = max i = 1 n max j = 1 n ( max k = 1 n ( max l = 1 n ( f i − 1 , j , k − 1 , l , f i , j − 1 , k − 1 , l , f i − 1 , j , k , l − 1 , f i , j − 1 , k , l − 1 ) ) )
树网的核
很简单,跑一遍Floyd
,再用一些奇妙的公式即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <bits/stdc++.h> using namespace std;int n, s, ans = INT_MAX;int g[700 ][700 ];void floyd () { for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) for (int k = 1 ; k <= n; k++) if (i != j && j != k && i != k) g[i][j] = min (g[i][j], g[i][k] + g[k][j]), g[j][i] = g[i][j]; } int main () {#ifdef ONLINE_JUDGE freopen ("core.in" , "r" , stdin); freopen ("core.out" , "w" , stdout); #endif cin >> n >> s; memset (g, 0x3f , sizeof (g)); for (int i = 1 ; i <= n; i++) g[i][i] = 0 ; for (int i = 1 ; i <= n; i++) { int x, y, z; cin >> x >> y >> z; g[x][y] = g[y][x] = z; } floyd (); for (int i = 1 ; i <= n; i++) for (int j = 1 ; j <= n; j++) if (g[i][j] <= s) { int p = INT_MIN; for (int k = 1 ; k <= n; k++) p = max (p, (g[i][k] + g[j][k] - g[i][j]) / 2 ); ans = min (ans, p); } cout << ans; return 0 ; }
你问我公式怎么来?好比这个图
i k + j k = i n + k n + j n + k n \large{ik+jk=in+kn+jn+kn} i k + j k = i n + k n + j n + k n
i j + 2 k w = i k + j k \large{ij+2kw=ik+jk} i j + 2 k w = i k + j k
k w = ( i k + j k − i j ) ÷ 2 \large{kw=(ik+jk-ij) \div 2} k w = ( i k + j k − i j ) ÷ 2
2024.7.12
比赛传送门
潜伏者
纯水题,看懂题目都做得对。
Hankson 的趣味题
暴力做法:枚举 x x x 到 b 1 b_1 b 1 ,按题目计算即可,期望得分: 50 50 5 0 分。
已知:b 1 ≤ 2 , 000 , 000 , 000 b_1≤2,000,000,000 b 1 ≤ 2 , 0 0 0 , 0 0 0 , 0 0 0 , n ≤ 2000 n≤2000 n ≤ 2 0 0 0 ,于是暴力的时间复杂度是 O ( 2 , 000 , 000 , 00 0 2000 ) \mathcal {O(2,000,000,000 ^ {2000})} O ( 2 , 0 0 0 , 0 0 0 , 0 0 0 2 0 0 0 ) 。
于是我们枚举到 b i \sqrt{b_i} b i ,因为两个数议定书两个数的最小公倍数的因子,于是我们将 b i ÷ x b_i \div x b i ÷ x 和 x ÷ b i x \div b_i x ÷ b i 分开计算。
注意: 计算的前提条件是 b i m o d x = 0 b_i \mod x = 0 b i m o d x = 0 的,且如果 b i ÷ x = x ÷ b i b_i \div x = x \div b_i b i ÷ x = x ÷ b i 时,只能计算一次。
最优贸易
此题奆说要使用 tarjan \textrm {tarjan} tarjan ,但是其实可以使用 SPFA \textrm {SPFA} SPFA 进行解题。
我们分别跑两次 SPFA \textrm {SPFA} SPFA 分别是从前往后,从后往前,注意,如果是从后往前,就需要反向建边,最后正常 SPFA \textrm {SPFA} SPFA 即可。
松弛部分(SPFA2相反即可):
1 2 3 4 5 6 7 8 9 10 int v = g1[i].to;if (dis1[v] > min (dis1[u], a[v])){ dis1[v] = min (dis1[u], a[v]); if (!bz[v]) { q.push (v); bz[v] = 1 ; } }
答案 = max i = 1 n ( d i s 2 i − d i s 1 j )
答案=\max_{i=1}^n(dis2_i-dis1_j)
答 案 = max i = 1 n ( d i s 2 i − d i s 1 j )
靶形数独
没有所谓正解,暴搜期望得分:80 80 8 0 ,模拟赛时因为一些奇奇怪怪的 BUG \textrm {BUG} BUG 没有拿到 80 80 8 0 分。
100 100 1 0 0 分也只是在 80 80 8 0 分的基础上优化而已:我们优先选择每行 0 0 0 少的点,因为数独的特性,0 0 0 少也就意味着可选择的数字少,递归层数也少,自然就可以过了。
处理小方块有点难想,但是画个图即可理解:
我们可以把 3 × 3 3 \times 3 3 × 3 的格子压成 1 × 1 1 \times 1 1 × 1 的格子,就像这样:
1 2 3 4 5 6 7 8 9 long long xx, yy;if (x % 3 == 0 ) xx = x / 3 ; else xx = x / 3 + 1 ; if (y % 3 == 0 ) yy = y / 3 ; else yy = y / 3 + 1 ;
2024.7.15
数位DP
【ZJOI2010】数字计数
数位 DP \textrm {DP} DP 板子题,问的是两个正整数 a a a 和 b b b ,求在 [ a , b ] [a,b] [ a , b ] 中的所有整数中,分别表示 0 ∼ 9 0 \thicksim 9 0 ∼ 9 在 [ a , b ] [a,b] [ a , b ] 中出现了多少次。
怎么做呢?
由于 a ≤ b ≤ 1 0 12 a \le b \le 10^{12} a ≤ b ≤ 1 0 1 2 ,a a a 和 b b b 太大了,如果暴力枚举肯定不能拿满分,于是我们需要新的算法:数位 DP \textrm {DP} DP
。
什么是数位 DP \textrm {DP} DP ?
数位是指把一个数字按照个、十、百、千等等一位一位地拆开,关注它每一位上的数字。如果拆的是十进制数,那么每一位数字都是 0 ∼ 9 0 \thicksim 9 0 ∼ 9 ,其他进制可类比十进制。
数位 DP \textrm {DP} DP :用来解决一类特定问题,这种问题比较好辨认,一般具有这几个特征:
要求统计满足一定条件的数的数量(即,最终目的为计数)
这些条件经过转化后可以使用「数位」的思想去理解和判断;
输入会提供一个数字区间(有时也只提供上界)来作为统计的限制;
上界很大(比如 1 0 18 10^{18} 1 0 1 8 ),暴力枚举验证会超时。
数位 DP 的基本原理:
考虑人类计数的方式,最朴素的计数就是从小到大开始依次加一。但我们发现对于位数比较多的数,这样的过程中有许多重复的部分。例如,从 7000 数到 7999、从 8000 数到 8999、和从 9000 数到 9999 的过程非常相似,它们都是后三位从 000 变到 999,不一样的地方只有千位这一位,所以我们可以把这些过程归并起来,将这些过程中产生的计数答案也都存在一个通用的数组里。此数组根据题目具体要求设置状态,用递推或 DP 的方式进行状态转移。
Code(注释版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 20 , D = 9 ;int L, R;int f[N + 2 ][D + 2 ][3 ][3 ] , g[N + 2 ][3 ], ans[D + 2 ], Pow[N + 10 ];int dfs (int x , int h, int d , bool flag , bool f0 ) { if (h == 0 ) { g[h][flag] = 1 ; return 0 ; } if (f[h][d][flag][f0] != -1 ) return f[h][d][flag][f0]; f[h][d][flag][f0] = g[h][flag] = 0 ; int up = flag == 1 ? x / Pow[h - 1 ] % 10 : 9 ; for (int i = 0 ; i <= up; i++) { f[h][d][flag][f0] += dfs (x, h - 1 , d, flag && (i == up) , f0 && (i == 0 ) ); g[h][flag] += g[h - 1 ][flag && (i == up)]; if (i == d && (d != 0 || !f0) ) f[h][d][flag][f0] += g[h - 1 ][flag && (i == up)]; } return f[h][d][flag][f0]; } int calc (int x) { int ans = 0 ; while (x) x /= 10 , ans++; return ans; } main (){ scanf ("%lld %lld" , &L, &R); Pow[0 ] = 1 ; for (int i = 1 ; i <= 18 ; i++) Pow[i] = Pow[i - 1 ] * 10 ; memset (f, -1 , sizeof (f)) , memset (g, 0 , sizeof (g)); int len = calc (R); for (int i = 0 ; i <= 9 ; i++) ans[i] += dfs (R, len, i, true , true ); if (L != 0 ) { memset (f, -1 , sizeof (f)), memset (g, 0 , sizeof (g)); len = calc (L - 1 ); for (int i = 0 ; i <= 9 ; i++) ans[i] -= dfs (L - 1 , len, i, true , true ); } for (int i = 0 ; i <= 9 ; i++) printf ("%lld " , ans[i]); return 0 ; }
链表模板
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 #include <bits/stdc++.h> using namespace std;struct Node { int value; Node *next; }; void insertNode (int i,Node *p) { Node *node = new Node; node -> value = i; node -> next = p -> next; p -> next = node; } void deleteNode (Node *p) { p -> value = p -> next -> value; Node *t = p -> next; p -> next = p -> next -> next; delete t; } int main () { return 0 ; }
2024.7.29
【NOIP2010tj】机器翻译
纯水题,模拟队列 就过了。
乌龟棋
比赛时动态规划没有打出来,暴搜拿了 20 20 2 0 分,实际上十分简单:
观察题目可得:每种类型的卡片上只有1 1 1 、2 2 2 、3 3 3 、4 4 4 四个数字之一
于是可以得出状态:
f i 1 , i 2 , i 3 , i 4 表示数值为 1 的的卡牌用了 i 1 次,数值 2 的卡牌用了 i 2 次,以此类推 …
f_{i_1,i_2,i_3,i_4}表示数值为1的的卡牌用了i_1次,数值2的卡牌用了i_2次,以此类推\dots
f i 1 , i 2 , i 3 , i 4 表 示 数 值 为 1 的 的 卡 牌 用 了 i 1 次 , 数 值 2 的 卡 牌 用 了 i 2 次 , 以 此 类 推 … 那么我们很轻松就可以得出转移方程(a a a 表示输入的第二行):
f i 1 , i 2 , i 3 , i 4 = max ( f i 1 , i 2 , i 3 , i 4 , f i 1 − 1 , i 2 , i 3 , i 4 + a ( 1 + i 1 + i 2 × 2 + i 3 × 3 + i 4 × 4 ) )
f_{i_1,i_2,i_3,i_4}=\max(f_{i_1,i_2,i_3,i_4},f_{i_1-1,i_2,i_3,i_4}+a_{(1 + i_1 + i_2 \times 2 + i_3 \times 3 + i_4 \times 4)})
f i 1 , i 2 , i 3 , i 4 = max ( f i 1 , i 2 , i 3 , i 4 , f i 1 − 1 , i 2 , i 3 , i 4 + a ( 1 + i 1 + i 2 × 2 + i 3 × 3 + i 4 × 4 ) )
剩下的可以以此类推:
1 2 3 4 5 6 7 8 9 10 f[0 ][0 ][0 ][0 ] = a[1 ]; for (int a1 = 0 ; a1 <= _[1 ]; a1++) for (int a2 = 0 ; a2 <= _[2 ]; a2++) for (int a3 = 0 ; a3 <= _[3 ]; a3++) for (int a4 = 0 ; a4 <= _[4 ]; a4++) { if (a1 > 0 ) f[a1][a2][a3][a4] = max (f[a1 - 1 ][a2][a3][a4] + a[1 + a1 + a2 * 2 + a3 * 3 + a4 * 4 ], f[a1][a2][a3][a4]); if (a2 > 0 ) f[a1][a2][a3][a4] = max (f[a1][a2 - 1 ][a3][a4] + a[1 + a1 + a2 * 2 + a3 * 3 + a4 * 4 ], f[a1][a2][a3][a4]); if (a3 > 0 ) f[a1][a2][a3][a4] = max (f[a1][a2][a3 - 1 ][a4] + a[1 + a1 + a2 * 2 + a3 * 3 + a4 * 4 ], f[a1][a2][a3][a4]); if (a4 > 0 ) f[a1][a2][a3][a4] = max (f[a1][a2][a3][a4 - 1 ] + a[1 + a1 + a2 * 2 + a3 * 3 + a4 * 4 ], f[a1][a2][a3][a4]); }
引水入城
这是一道搜索题,实现不难,但是比较难想。
第一步
我们先对第一行的每一个点进行 DFS ,把走过的点进行标记,看最后一行是否全都被标记,如果没有,输出 0 0 0 。
第二步
接下来我们要在 DFS 中进行 r r r 数组和 l l l 数组的维护:
r x , y r_{x,y} r x , y 表示的是以坐标为 x , y x,y x , y 的点出发,可以到达的最右侧的点
l x , y l_{x,y} l x , y 表示的是以坐标为 x , y x,y x , y 的点出发,可以到达的最坐侧的点
例如在这个图中: l x , y = 2 l_{x,y}=2 l x , y = 2 就是蓝色那个点,r x , y = 4 r_{x,y}=4 r x , y = 4 就是红色那个点。
这个 DFS 过程中求即可
跳数
1 2 3 4 5 6 7 8 9 int k = 1 ;while (k <= m) { int cmp = 0 ; ans++; for (int i = 1 ; i <= m; i++) if (l[1 ][i]<=k) cmp = max (cmp, r[1 ][i]); k = cmp + 1 ; }
关押罪犯
这是一道并查集 ,我们将人分为两类,一类是互相是仇人,一类是互相是 “友人” ,我们通过并查集进行维护:
先将输入的数组的权值进行从大到小的排序,然后开始维护:
1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i = 1 ; i <= m; i++) { int fx = find (g[i].a); int fy = find (g[i].b); if (fx == fy) { cout << g[i].c; return 0 ; } if (fx != fy) { f[fx] = find (g[i].b + n); f[fy] = find (g[i].a + n); } } cout << 0 ;
2024.7.31
【NOIP2012提高组】同余方程
这题是一道数论题,考时没有学过,暴力拿了 60 60 6 0 分(谁说信息学与数学无关),下面开始解题:
a x + b y = c ( a , b 为常数) gcd ( a , b ) + ( a gcd ( a , b ) x + b gcd ( a , b ) y ) = c (乘法分配律) ∵ z + a x + b y = gcd ( a , b ) + z = c ( z 是一个常数) ∴ a x + b y = gcd ( a , b ) a x + b y = gcd ( a , b ) ① a x ≡ 1 ( x m o d b ) a x + b y = 1 a x + b y = gcd ( a , b ) gcd ( a , b ) = gcd ( b , a m o d b ) a x + b y = gcd ( a , b ) b x ′ + ( a m o d b ) y ′ = gcd ( a , b ) b x ′ + ( a − ⌊ a b b ⌋ ) y ′ = gcd ( a , b ) b x ′ + a y ′ − ⌊ a b b ⌋ y ′ = gcd ( a , b ) a y ′ − ( x ′ − ⌊ a b ⌋ y ′ ) b = gcd ( a , b ) = 1 ( 这个和公式①很像,于是可以得出: x = y ′ , y = x ′ − ⌊ a b ⌋ y ′ )
\begin{aligned}
ax~+~by~&=~c(a,~b~为常数)\\
\gcd\left(a,~b\right)+\left(\frac{a}{\gcd(a,~b)}x + \frac{b}{\gcd(a,~b)}y\right)~&=~c(乘法分配律)\\
\because z~+~ax~+~by~&=~\gcd(a,~b)~+~z~=~c(z~是一个常数)\\
\therefore ax~+~by~=~\gcd(a,~b)\\
ax~+~by~&=~\gcd(a,~b)①\\
ax~\equiv 1(x\mod b)\\
ax~+~by~&=~1\\
ax~+~by~&=~\gcd(a,b)\\
\gcd(a,~b)~&=~\gcd(b,a\mod b)\\
ax+by&=\gcd(a,b)\\
bx'+(a\mod b)y'&=\gcd(a,b)\\
bx'+(a-\lfloor\frac{a}{b}b\rfloor) y'&=\gcd(a,b)\\
bx'+ay'-\lfloor\frac{a}{b}b\rfloor y' &=\gcd(a,b)\\
ay'-(x'-\lfloor\frac{a}{b}\rfloor y')b&=\gcd(a,b) = 1\\(这个和公式①很像,于是可以得出:&x=y',y=x'-\lfloor\frac{a}{b}\rfloor y')
\end{aligned}
a x + b y g cd( a , b ) + ( g cd( a , b ) a x + g cd( a , b ) b y ) ∵ z + a x + b y ∴ a x + b y = g cd( a , b ) a x + b y a x ≡ 1 ( x m o d b ) a x + b y a x + b y g cd( a , b ) a x + b y b x ′ + ( a m o d b ) y ′ b x ′ + ( a − ⌊ b a b ⌋ ) y ′ b x ′ + a y ′ − ⌊ b a b ⌋ y ′ a y ′ − ( x ′ − ⌊ b a ⌋ y ′ ) b ( 这 个 和 公 式 ① 很 像 , 于 是 可 以 得 出 : = c ( a , b 为 常 数 ) = c ( 乘 法 分 配 律 ) = g cd( a , b ) + z = c ( z 是 一 个 常 数 ) = g cd( a , b ) ① = 1 = g cd( a , b ) = g cd( b , a m o d b ) = g cd( a , b ) = g cd( a , b ) = g cd( a , b ) = g cd( a , b ) = g cd( a , b ) = 1 x = y ′ , y = x ′ − ⌊ b a ⌋ y ′ )
借教室
赛时 90 90 9 0 分,卡卡常就可以过,线段树板子。
2024.8.3
D - AtCoder Janken 3题解
题意
高桥和青木要玩石头剪刀布,给你一个长度为 n n n 的字符串 s s s ,s s s 表示青木在第 i i i 局游戏中的动作(R
表示石头,P
表示布,S
表示剪刀)。
高桥不可以在任何一局中输给青木 (即:高桥和青木只可以平局或高桥赢青木),且高桥第 i i i 局出的和第 i − 1 i-1 i − 1 局出的不能一样,现在,问你高桥可以胜几局?
解题思路
很明显是一道 DP 题。
定义状态
f i , j f_{i,j} f i , j 表示第 i i i 局出 j j j 时最多可以获胜的局数(j = 1 j = 1 j = 1 时表示出石头,j = 2 j = 2 j = 2 时表示出布,j = 3 j = 3 j = 3 时表示出剪刀)。
初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 if (s[0 ] == 'R' ) { f[0 ][1 ] = 0 ; f[0 ][2 ] = 1 ; } if (s[0 ] == 'P' ) { f[0 ][2 ] = 0 ; f[0 ][3 ] = 1 ; } if (s[0 ] == 'S' ) { f[0 ][1 ] = 1 ; f[0 ][3 ] = 0 ; }
转移方程:
当 s i = R s_i =\textrm{R} s i = R 时:
f i , 1 = max ( f i − 1 , 2 , f i − 1 , 3 ) f i , 2 = max ( f i − 1 , 1 , f i − 1 , 3 ) + 1
f_{i,1} = \max(f_{i-1,2}, f_{i-1,3})\\
f_{i,2} = \max(f_{i-1,1}, f_{i-1,3}) + 1
f i , 1 = max ( f i − 1 , 2 , f i − 1 , 3 ) f i , 2 = max ( f i − 1 , 1 , f i − 1 , 3 ) + 1 (注意:f i , 1 f_{i,1} f i , 1 是被 f i − 1 , 2 f_{i-1,2} f i − 1 , 2 和 f i − 1 , 3 f_{i-1,3} f i − 1 , 3 转移过来的,因为高桥第 i i i 局出的和第 i − 1 i-1 i − 1 局出的不能一样,
高桥不可以输给青木 ,所以 j j j 不可以取 3 3 3 ,即:不可以出剪刀)。
当 s i = P s_i =\textrm{P} s i = P 时:
f i , 2 = max ( f i − 1 , 1 , f i − 1 , 3 ) f i , 3 = max ( f i − 1 , 2 , f i − 1 , 3 ) + 1
f_{i,2} = \max(f_{i-1,1}, f_{i-1,3})\\
f_{i,3} = \max(f_{i-1,2}, f_{i-1,3}) + 1
f i , 2 = max ( f i − 1 , 1 , f i − 1 , 3 ) f i , 3 = max ( f i − 1 , 2 , f i − 1 , 3 ) + 1 当 s i = S s_i =\textrm{S} s i = S 时:
f i , 1 = max ( f i − 1 , 2 , f i − 1 , 3 ) + 1 f i , 3 = max ( f i − 1 , 1 , f i − 1 , 2 )
f_{i,1} = \max(f_{i-1,2}, f_{i-1,3}) + 1\\
f_{i,3} = \max(f_{i-1,1}, f_{i-1,2})
f i , 1 = max ( f i − 1 , 2 , f i − 1 , 3 ) + 1 f i , 3 = max ( f i − 1 , 1 , f i − 1 , 2 ) 答案就取 max ( f n − 1 , 1 , f n − 1 , 2 , f n − 1 , 3 ) \max(f_{n - 1,1},f_{n - 1,2},f_{n - 1,3}) max ( f n − 1 , 1 , f n − 1 , 2 , f n − 1 , 3 ) ,(我的字符串 s s s 下标从 0 0 0 开始存)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <bits/stdc++.h> #define int long long using namespace std;const int N = 2e5 + 10 ;int n, ans, f[N][10 ];string s; main () { cin >> n >> s; if (s[0 ] == 'R' ) { f[0 ][1 ] = 0 ; f[0 ][2 ] = 1 ; } if (s[0 ] == 'P' ) { f[0 ][2 ] = 0 ; f[0 ][3 ] = 1 ; } if (s[0 ] == 'S' ) { f[0 ][1 ] = 1 ; f[0 ][3 ] = 0 ; } for (int i = 1 ; i < n; i++) { if (s[i] == 'R' ) { f[i][1 ] = max (f[i - 1 ][2 ], f[i - 1 ][3 ]); f[i][2 ] = max (f[i - 1 ][1 ], f[i - 1 ][3 ]) + 1 ; } if (s[i] == 'P' ) { f[i][2 ] = max (f[i - 1 ][1 ], f[i - 1 ][3 ]); f[i][3 ] = max (f[i - 1 ][1 ], f[i - 1 ][2 ]) + 1 ; } if (s[i] == 'S' ) { f[i][1 ] = max (f[i - 1 ][2 ], f[i - 1 ][3 ]) + 1 ; f[i][3 ] = max (f[i - 1 ][1 ], f[i - 1 ][2 ]); } } cout << max ({f[n - 1 ][1 ], f[n - 1 ][2 ], f[n - 1 ][3 ]}); return 0 ; }