2025 11联测4

井字棋与算法问题分析

井字棋

 文件 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~#31≤T≤10一个局面中至多存在四个字符为 x 或 o
#4~#101≤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

提示/说明

大样例数据下载

(记得解压)

样例解释

删去的部分用括号表示

下面给出对样例中的例子的构造方法,其中有些数据构造方案不唯一,仅展示其中一种可行的构造方法。

  1. 第一组:101110110 -> (10)111011(0)
    移除的 1 有 1 个,剩余的 0 有 1 个,较大值为 1

  2. 第二组:1001001001001 -> (100100)1001(001)
    移除的 1 有 3 个,剩余的 0 有 2 个,较大值为 3

  3. 第三组:0000111111 -> (0000)111111()
    移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0

  4. 第四组:00000 -> (00000)()
    移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0

  5. 第五组:1111 -> ()1111()
    移除的 1 有 0 个,剩余的 0 有 0 个,较大值为 0

数据范围

共 10 个测试点,每个测试点 10 分。

测试点编号t 数据范围字符串长度 len其他说明
1 ~ 31≤t≤101≤len≤100
4 ~ 51≤t≤101≤len≤20,000每个字符串中 1 的数量不超过 10 个
6 ~ 71≤t≤101≤len≤20,000每个字符串中 0 的数量不超过 10 个
8 ~ 101≤t≤101≤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;
 }

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值