[BZOJ4318]OSU! 题解
题目地址:BZOJ:Problem 4318. — OSU! 题目描述 osu …
May all the beauty be blessed.
题目地址:Codeforces:Problem – 5C – Codeforces、洛谷:CF5C Longest Regular Bracket Sequence – 洛谷 | 计算机科学教育新生态
This is yet another problem dealing with regular bracket sequences.
We should remind you that a bracket sequence is called regular, if by inserting «+» and «1» into it we can get a correct mathematical expression. For example, sequences «(())()», «()» and «(()(()))» are regular, while «)(», «(()» and «(()))(» are not.
You are given a string of «(» and «)» characters. You are to find its longest substring that is a regular bracket sequence. You are to find the number of such substrings as well.
给一个括号序列,求其最长的完全匹配连续子串长度及数量。
输入格式:
The first line of the input file contains a non-empty string, consisting of «(» and «)» characters. Its length does not exceed 10^6.
输出格式:
Print the length of the longest substring that is a regular bracket sequence, and the number of such substrings. If there are no such substrings, write the only line containing “0 1”.
输入样例#1:
)((())))(()())
输出样例#1:
6 2
输入样例#2:
))(
输出样例#2:
0 1
求括号序列匹配的常用方式是利用栈,即遇到一个左括号将其放入栈中,遇到一个右括号取栈顶元素并将这一对括号设置为已匹配。如果中间出现了未匹配段,则一定是右括号有多的,因此连续的已匹配段构成的子串一定是完全匹配子串。计算出匹配情况后,统计连续匹配的最长段及其数量即可。
// Code by KSkun, 2019/6
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <stack>
const int MAXN = 1000005;
int n;
bool match[MAXN];
char s[MAXN];
std::stack<int> sta;
int main() {
scanf("%s", s + 1);
n = strlen(s + 1);
for(int i = 1; i <= n; i++) {
if(s[i] == '(') sta.push(i);
else {
if(sta.empty()) continue;
int l = sta.top(); sta.pop();
match[l] = match[i] = true;
}
}
int mx = 0, mxcnt = 1, now = 0;
for(int i = 1; i <= n; i++) {
if(match[i]) {
now++;
} else {
if(now > mx) {
mx = now; mxcnt = 1;
} else if(now == mx) {
mxcnt++;
}
now = 0;
}
}
if(now > mx) {
mx = now; mxcnt = 1;
} else if(now == mx) {
mxcnt++;
}
if(mx == 0) mxcnt = 1;
printf("%d %d", mx, mxcnt);
return 0;
}
题目地址:Codeforces:Problem – 3C – Codeforces、洛谷:CF3C Tic-tac-toe – 洛谷 | 计算机科学教育新生态
Certainly, everyone is familiar with tic-tac-toe game. The rules are very simple indeed. Two players take turns marking the cells in a 3 × 3 grid (one player always draws crosses, the other — noughts). The player who succeeds first in placing three of his marks in a horizontal, vertical or diagonal line wins, and the game is finished. The player who draws crosses goes first. If the grid is filled, but neither Xs, nor 0s form the required line, a draw is announced.
You are given a 3 × 3 grid, each grid cell is empty, or occupied by a cross or a nought. You have to find the player (first or second), whose turn is next, or print one of the verdicts below:
illegal
— if the given board layout can’t appear during a valid game;the first player won
— if in the given board layout the first player has just won;the second player won
— if in the given board layout the second player has just won;draw
— if the given board layout has just let to a draw.给一个井字棋(三子棋)的局面,请判断该局面的状态,状态有以下几种:
井字棋玩法请百度。
输入格式:
The input consists of three lines, each of the lines contains characters “.”, “X” or “0” (a period, a capital letter X, or a digit zero).
输出格式:
Print one of the six verdicts: first
, second
, illegal
, the first player won
, the second player won
or draw
.
输入样例#1:
X0X .0. .X.
输出样例#1:
second
大模拟题,细节和分类特别多。分状态讨论符合的情况。
下一步X下棋,仅出现于合法且无人获胜的局面中,特征是局面中X和O的数量相同。
下一步O下棋,仅出现于合法且无人获胜的局面中,特征是局面中X比O的数量多1。
非法状态,有以下几类:X与O的数量的关系既不是相同也不是X比O多1;X获胜之后O仍然下了一步棋(X获胜之后X与O数量相同);O获胜之后X仍然下了一步棋(O获胜之后X比O数量多1)。
X获胜,合法局面中X棋子占据了某一行、列或正副对角线。
O获胜,合法局面中O棋子占据了某一行、列或正副对角线。
平局,合法局面,无人获胜,且棋盘中没有空位。
调试的时候平均一个submission过一个测试点,果然还是我太菜了。
// Code by KSkun, 2019/6
#include <cstdio>
char a[5][5];
int main() {
scanf("%s%s%s", a[1] + 1, a[2] + 1, a[3] + 1);
int cntx = 0, cnto = 0;
bool hasdot = false;
for(int i = 1; i <= 3; i++) {
for(int j = 1; j <= 3; j++) {
if(a[i][j] == 'X') cntx++;
if(a[i][j] == '0') cnto++;
if(a[i][j] == '.') hasdot = true;
}
}
if(cntx != cnto && cntx != cnto + 1) {
puts("illegal"); return 0;
}
bool fw = false, sw = false;
for(int i = 1; i <= 3; i++) {
if(a[i][1] == a[i][2] && a[i][2] == a[i][3]) {
if(a[i][1] == 'X') fw = true;
else if(a[i][1] == '0') sw = true;
}
}
for(int i = 1; i <= 3; i++) {
if(a[1][i] == a[2][i] && a[2][i] == a[3][i]) {
if(a[1][i] == 'X') fw = true;
else if(a[1][i] == '0') sw = true;
}
}
if(a[1][1] == a[2][2] && a[2][2] == a[3][3]) {
if(a[1][1] == 'X') fw = true;
else if(a[1][1] == '0') sw = true;
}
if(a[1][3] == a[2][2] && a[2][2] == a[3][1]) {
if(a[1][3] == 'X') fw = true;
else if(a[1][3] == '0') sw = true;
}
if(fw && sw) puts("illegal");
else if(fw && cntx != cnto + 1) puts("illegal");
else if(sw && cntx != cnto) puts("illegal");
else if(fw) puts("the first player won");
else if(sw) puts("the second player won");
else if(!hasdot) puts("draw");
else if(cntx == cnto) puts("first");
else if(cntx == cnto + 1) puts("second");
return 0;
}
题目地址:Codeforces:Problem – 2B – Codeforces、洛谷:CF2B The least round way – 洛谷 | 计算机科学教育新生态
There is a square matrix n × n, consisting of non-negative integer numbers. You should find such a way on it that
Moreover, if we multiply together all the numbers along the way, the result should be the least “round”. In other words, it should end in the least possible number of zeros.
给定一个$n \times n$的非负整数矩阵,要求你找出一条从左上角到右下角的路径,使得路径上的数字相乘得到的积的后缀0数量最少。
输入格式:
The first line contains an integer number n (2 ≤ n ≤ 1000), n is the size of the matrix. Then follow n lines containing the matrix elements (non-negative integer numbers not exceeding 10^9).
输出格式:
In the first line print the least number of trailing zeros. In the second line print the correspondent way itself.
输入样例#1:
3 1 2 3 4 5 6 7 8 9
输出样例#1:
0 DDRR
考虑乘积的后缀0数量等于乘积中因数10的数量,则最小化乘积中质因子2和5的数量的思路很自然可以想出。答案即是最小化的2和5质因子数量中的较小值。
接下来考虑如何最小化答案。我们先用常规思路分别对质因子2和5的数量各自做一遍最小化,得到了两个答案,取其最小值即可。这个最小值一定可以取到,因为在取最小值时,最小化的那个质因子的数量一定比另外一个质因子小,这就保证了答案的正确性。
上面所说的“常规思路”是这样的一种动态规划思路:对于某一个点$(i, j)$,从左上角点$(1, 1)$到该处的路径上的最小质因子数量只能由左边的点$(i, j – 1)$和上面的点$(i – 1, j)$转移而来,即如果设$f(i, j)$是点$(i, j)$处的数字的某质因子数量,则上述状态的状态转移方程是
$$ dp(i, j) = f(i, j) + \min\{ dp(i – 1, j), dp(i, j – 1) \} $$
以为到这里就做完了?注意题目中有一个“非负整数”的坑,你还需要考虑路径中有0的情况。
如果路径中有0,会让路径的乘积变为0,即后缀0有1个。如果原本的最优路径的后缀0为0个,则原答案最优,否则应该经过这个数字为0的点。这个地方特例处理一下就可以了。
此外,注意处理一下边界情况无效状态不可转移即可。
算法总复杂度为$O(n^2)$。
// Code by KSkun, 2019/6
#include <cstdio>
#include <cctype>
#include <algorithm>
typedef long long LL;
inline char fgc() {
static char buf[100000], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2)
? EOF : *p1++;
}
inline LL readint() {
LL res = 0, neg = 1; char c = fgc();
for(; !isdigit(c); c = fgc()) if(c == '-') neg = -1;
for(; isdigit(c); c = fgc()) res = res * 10 + c - '0';
return res * neg;
}
inline char readsingle() {
char c;
while(!isgraph(c = fgc())) {}
return c;
}
const int MAXN = 1005;
int n, a[MAXN][MAXN], dp2[MAXN][MAXN], dp5[MAXN][MAXN];
char pre2[MAXN][MAXN], pre5[MAXN][MAXN], ans[MAXN << 1];
int atot = 0;
int zx = 0, zy = 0;
inline int calfac(int x, int f) {
if(x == 0) return 0; // 注意x=0时进入死循环
int cnt = 0;
while(x % f == 0) {
cnt++; x /= f;
}
return cnt;
}
int main() {
n = readint();
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
a[i][j] = readint();
if(a[i][j] == 0) {
zx = i; zy = j;
}
dp2[i][j] = calfac(a[i][j], 2);
dp5[i][j] = calfac(a[i][j], 5);
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int r1 = dp2[i][j] + dp2[i - 1][j], r2 = dp2[i][j] + dp2[i][j - 1];
if(i == 1) r1 = 1e9; // 边界情况无效状态设置为不可转移
if(j == 1) r2 = 1e9;
if(r1 < r2) {
dp2[i][j] += dp2[i - 1][j];
pre2[i][j] = 'D';
} else {
dp2[i][j] += dp2[i][j - 1];
pre2[i][j] = 'R';
}
}
}
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int r1 = dp5[i][j] + dp5[i - 1][j], r2 = dp5[i][j] + dp5[i][j - 1];
if(i == 1) r1 = 1e9; // 边界情况无效状态设置为不可转移
if(j == 1) r2 = 1e9;
if(r1 < r2) {
dp5[i][j] += dp5[i - 1][j];
pre5[i][j] = 'D';
} else {
dp5[i][j] += dp5[i][j - 1];
pre5[i][j] = 'R';
}
}
}
int t;
if(dp2[n][n] < dp5[n][n]) t = 2; else t = 5;
if(zx && zy && (t == 2 ? dp2[n][n] : dp5[n][n]) > 1) {
puts("1"); // 当包含0的路径是最优情况时的特殊处理
for(int i = 1; i < zx; i++) putchar('D');
for(int i = 1; i < zy; i++) putchar('R');
for(int i = zx; i < n; i++) putchar('D');
for(int i = zy; i < n; i++) putchar('R');
} else {
printf("%d\n", t == 2 ? dp2[n][n] : dp5[n][n]);
int nx = n, ny = n;
while(nx != 1 || ny != 1) {
char op = t == 2 ? pre2[nx][ny] : pre5[nx][ny];
ans[++atot] = op;
if(op == 'D') nx--;
else ny--;
}
for(int i = atot; i >= 1; i--) putchar(ans[i]);
}
return 0;
}