井字棋
文件 IO比赛题目
时间限制: 1000MS空间限制: 256MB
题目描述
小姜和王老师在下井字棋。
井字棋是在 3×3 的网格中,双方轮流放下棋子,抢先形成一行/一列/一组对角线同色棋子的玩家胜利。
- 小姜和王老师轮流将字符放入空位(
.)中,其中小姜先手。 - 小姜总是放字符
x,而王老师总是放字符o x和o只允许放置在空位中,不允许对已放有字符的位置进行填充。- 当有 3 个相同(且非空)的字符填充任何行、列或对角线时,游戏结束。
- 当所有位置非空时,也算为游戏结束。
- 如果游戏结束,玩家不允许再放置字符。
现在,给出井字棋的棋盘状态。
小姜和王老师对这盘棋的当前状态有争议,他们觉得这可能不是正常能够形成的游戏局面。
你的任务是帮助他们判断,能否通过正常的轮流走子形成当前的游戏局面。
- 若能,输出
True - 若不能,输出
False
下面举一些例子做说明:
...
..x
.o.
这是一个可能形成的局面。其中小姜在第二行第三列放了一个字符,之后王老师在第三行第二列。
x.o
x.o
x.o
这是一个不可能形成的局面。小姜连成三个之后游戏已经结束了,不可能还会继续让王老师放置字符。
.o.
.o.
.x.
这是一个不可能形成的局面,小姜作为先手方不可能放置一个字符而王老师放置了两个。
输入格式
第一行一个正整数 T,表示有 T 组局面等待你做判断。
接下来每三行描述一场棋盘状态。
每组局面和下一组之间有一个空行来帮助你区分开不同的局面。
输出格式
对每组局面,输出一行 True 或者 False 表示是否可行。
样例输入 1
3 ... ..x .o. x.o x.o x.o .o. .o. .x.
样例输出 1
True False False
提示/说明
数据范围
共 10 个测试点,每个测试点 10 分。
| 测试点编号 | T 数据范围 | 其他说明 |
|---|---|---|
| #1~#3 | 1≤T≤10 | 一个局面中至多存在四个字符为 x 或 o |
| #4~#10 | 1≤T≤1000 | 无 |
代码:
#include <bits/stdc++.h>
using namespace std;
char board[5][5];
bool judge() {
int count_x = 0, count_o = 0;
for(int i = 1;i <= 3; ++i)
for(int j = 1;j <= 3; ++j)
if(board[i][j] == 'x') ++count_x;
else if(board[i][j] == 'o') ++count_o;
if(!(count_x - 1 <= count_o && count_o <= count_x)) return false;
// 先判断棋⼦数量是否合法,x先下,o后下,因此 x棋⼦数量 - 1 <= o的棋⼦数量 <= x棋⼦数量
// 不满⾜就return false
int win_x = 0, win_o = 0;
for(int i = 1;i <= 3; ++i)
if(board[i][1] == board[i][2] && board[i][2] == board[i][3])
if(board[i][1] == 'x') ++win_x;
else if(board[i][1] == 'o') ++win_o;
for(int i = 1;i <= 3; ++i)
if(board[1][i] == board[2][i] && board[2][i] == board[3][i])
if(board[1][i] == 'x') ++win_x;
else if(board[1][i] == 'o') ++win_o;
if(board[1][1] == board[2][2] && board[2][2] == board[3][3])
if(board[1][1] == 'x') ++win_x;
else if(board[1][1] == 'o') ++win_o;
if(board[3][1] == board[2][2] && board[2][2] == board[1][3])
if(board[3][1] == 'x') ++win_x;
else if(board[3][1] == 'o') ++win_o;
if(win_x && win_o) return false;
// 接下来最多有⼀个⼈赢则局⾯结束,不可能俩⼈都赢
// 不满⾜就return false
if(win_x == 1 && count_o != count_x - 1) return false;
if(win_o == 1 && count_o != count_x) return false;
// 接下来考虑输赢终局时的棋⼦数量问题,当x赢的时候,x⽐o多⼀个棋⼦
// 当o赢的时候,x和o的棋⼦数量⼀样,不满⾜也要return false
return true;
}
int main(int argc, char **argv) {
int T; scanf("%d", &T);
while(T--) {
for(int i = 1;i <= 3; ++i)
scanf("%s", board[i] + 1);
if(judge()) {
printf("True\n");
} else {
printf("False\n");
}
}
}
王老师的不同
文件 IO比赛题目
时间限制: 1000MS空间限制: 256MB
题目描述
王老师有一个长度为 n 的序列 a,其中 ai∈[1,k],并且 1∼k 在 a 中都至少出现了一次。
王老师通过序列 a 计算出来另外一个序列 b:bi=minj∈[1,n],aj=ai∣i−j∣,即 ai 到最近的不同的数字 aj 的距离。
王老师想要知道,对于所有的序列 a,能够计算出多少个不同的序列 b ?由于答案可能很大,只需要输出答案对 998244353 取模后的值。
输入格式
输入的第一行包含两个整数 n,k。
输出格式
共一行,输出一个整数。
样例输入 1
2 2
样例输出 1
1
样例输入 2
6 5
样例输出 2
3
样例输入 3
见考生文件夹中的下发大样例 difference3.in
样例输出 3
见考生文件夹中的下发大样例 difference3.out
样例输入 4
见考生文件夹中的下发大样例 difference4.in
样例输出 4
见考生文件夹中的下发大样例 difference4.out
提示/说明
样例 1 解释
只有 b1=1,b2=1 一种可能的序列 b。
大样例下载
数据规模与约定
- 对于 20% 的数据,保证 n,k≤5。
- 对于 40% 的数据,保证 n≤500。
- 对于 60% 的数据,保证 n≤5000。
- 对于另 20% 的数据,保证 k=2。
- 对于 100% 的数据,保证 1≤n≤2×105,2≤k≤min(n,10)。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+7,K=15,p=998244353;
int n,k,f[N][K],g[N][K];
int add(int a,int b){
if(a+b>=p) return a+b-p; return a+b;
}
int dec(int a,int b){
if(a-b<0) return a-b+p; return a-b;
}
inline void solve(){
cin>>n>>k;
f[0][0]=g[0][0]=1;
for(int i=1;i<=n;i++){
g[i][0]=1;
for(int j=1;j<=k;j++){
f[i][j]=g[i-1][j-1];
if(i>2&&i!=n)f[i][j]=dec(f[i][j],f[i-2][j-1]);
g[i][j]=add(g[i-1][j],f[i][j]);
}
f[i][k+1]=add(g[i-1][k],g[i-1][k+1]);
if(i>2&&i!=n)f[i][k+1]=dec(f[i][k+1],add(f[i-2][k],f[i-2][k+1]));
g[i][k+1]=add(g[i-1][k+1],f[i][k+1]);
}
cout<<add(f[n][k],f[n][k+1])<<endl;
}
signed main(){
solve();
return 0;
}
王老师的最少代价
文件 IO比赛题目
时间限制: 1000MS空间限制: 256MB
题目描述
王老师有一个只包含 0 和 1 的字符串 S。
王老师准备对字符串 S 进行如下操作:
- 先从 字符串的开头 移除若干个字符(可以为 0 个)。
- 再从 字符串的结尾 移除若干个字符(可以为 0 个)。
操作后,S 会变成一个连续的子串(可以是原串或者空串,但空串通常不会有最小代价意义)。
定义本次操作的代价为:
- 移除的 1 的个数 与 剩余字符串中的 0 的个数,两者中的较大值。
请你帮王老师计算: 所有可能的操作方式中,最小的代价是多少?
输入格式
第一行一个整数 t,表示数据组数。
接下来 t 行,每行一个字符串 S。
输出格式
对每组数据,输出一个整数表示最小代价。
样例输入 1
5 101110110 1001001001001 0000111111 00000 1111
样例输出 1
1 3 0 0 0
样例输入 2
见题目中的大样例数据cost.in
样例输出 2
见题目中的大样例数据cost.out
提示/说明
大样例数据下载
(记得解压)
样例解释
删去的部分用括号表示
下面给出对样例中的例子的构造方法,其中有些数据构造方案不唯一,仅展示其中一种可行的构造方法。
-
第一组:101110110 -> (10)111011(0)
移除的 1 有 1 个,剩余的 0 有 1 个,较大值为 1 -
第二组:1001001001001 -> (100100)1001(001)
移除的 1 有 3 个,剩余的 0 有 2 个,较大值为 3 -
第三组:0000111111 -> (0000)111111()
移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0 -
第四组:00000 -> (00000)()
移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0 -
第五组:1111 -> ()1111()
移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0
数据范围
共 10 个测试点,每个测试点 10 分。
| 测试点编号 | t 数据范围 | 字符串长度 len | 其他说明 |
|---|---|---|---|
| 1 ~ 3 | 1≤t≤10 | 1≤len≤100 | 无 |
| 4 ~ 5 | 1≤t≤10 | 1≤len≤20,000 | 每个字符串中 1 的数量不超过 10 个 |
| 6 ~ 7 | 1≤t≤10 | 1≤len≤20,000 | 每个字符串中 0 的数量不超过 10 个 |
| 8 ~ 10 | 1≤t≤10 | 1≤len≤20,000 |
无 |
代码:
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int sum0[N], sum1[N];
char s[N];
int main() {
freopen("cost.in", "r", stdin);
freopen("cost.out", "w", stdout);
int t;
cin >> t;
while (t--) {
cin >> s + 1;
int n = strlen(s + 1), ans = 1e9;
for (int i = 1; i <= n; i++) {
sum0[i] = sum0[i-1] + (s[i]=='0');
sum1[i] = sum1[i-1] + (s[i]=='1');
}
int r = n + 1;
for (int i = 1; i <= n; i++) {
if (sum0[i-1] == sum1[n] - sum1[i-1]) {
r = i;
ans = sum0[i-1];
break;
}
}
for (int i = 0; i <= n; i++) {
while (r <= n && sum1[i] + (sum1[n] - sum1[r-1]) > sum0[r-1]-sum0[i]) {
r++;
}
ans = min(ans, sum1[i] + (sum1[n] - sum1[r-1]));
}
cout << ans << '\n';
}
return 0;
}
井字棋与算法问题分析
122

被折叠的 条评论
为什么被折叠?



