返回题解分享
讨论 / 题解分享/ 帖子详情

五子棋对弈(结果填空) - 题解

首先思考是不是可以直接通过枚举情况求解出来 可以发现是不行的 不存在通式,于是只能采取模拟的方法 问题的关键在于模拟一个组合数的排列情况
这里我给出两种 递归/字典序生成的方法
前者相当好理解 后者更方便且有stl的现成函数使用
代码如下

#include<bits/stdc++.h>
using namespace std;

#define int long long
#define pii pair long long
#define endl "\n"

int sy=2000,sm=1,sd=1;
int ey=2024,em=4,ed=13;
int cy,cm,cd;
int nums[10]={13,1,2,3,5,4,4,2,2,2};
int ans=0;
void date() {
    cy = sy;
    cm = sm;
    cd = sd;
    int month_day_local[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    
    while (true) {
        // 计算字符串
        string s = to_string(cy) + (cm < 10 ? "0" : "") + to_string(cm) + (cd < 10 ? "0" : "") + to_string(cd);
        int sum = 0;
        for (char ch : s) sum += nums[ch - '0'];
        if (sum > 50) ans++;
        
        if (cy == ey && cm == em && cd == ed) break;
        
        // 更新日期
        month_day_local[2] = (cy % 400 == 0 || (cy % 4 == 0 && cy % 100 != 0)) ? 29 : 28;
        cd++;
        if (cd > month_day_local[cm]) { cm++; cd = 1; }
        if (cm > 12) { cm = 1; cy++; }
    }
}

int zh(int n, int m) {
    if (m > n - m) m = n - m;  // C(n, m) = C(n, n-m),减少乘法次数
    int ans = 1;
//    c_n_k+1=c_n_k*(n-k/j+1) 因此必然是整数
    for (int i = 0; i < m; i++) {
        ans *= (n - i);
        ans /= (i + 1); //必须分子先乘大的 分母除以小的 这样优化不会导致出问题
    }
    return ans;
}


int check(vector<int>& v) {
    // 直接用 1D 数组访问,不用构造 2D 数组
    int flag_r = 1, flag_c = 1, flag_x1 = 1, flag_x2 = 1;
    
    for (int i = 0; i < 5; i++) {
        int t = v[i * 5];
        if (t == v[i * 5 + 1] && t == v[i * 5 + 2] && t == v[i * 5 + 3] && t == v[i * 5 + 4]) flag_r = 0;
        t = v[i];
        if (t == v[i + 5] && t == v[i + 10] && t == v[i + 15] && t == v[i + 20]) flag_c = 0;
    }
    
    int t = v[0];
    if (t == v[6] && t == v[12] && t == v[18] && t == v[24]) flag_x1 = 0;
    t = v[4];
    if (t == v[8] && t == v[12] && t == v[16] && t == v[20]) flag_x2 = 0;
    
    return (flag_r && flag_c && flag_x1 && flag_x2);
}

void next_com(vector<int>& v,int start,int cnt){
    //通过递归的思想 此时我们的思路就是 从后去想  尽量不重复
    if(cnt==0){
        //此时达到了一种排序 终止条件 选到了对应数量的
        //此时我们的v 是一种排序 直接映射到棋盘上 检测是否可以
        if(check(v)) ans++;
//        for(auto it:v) cout<<it;
//        cout<<endl;
        return ;
    }
    int n=v.size();
    for(int i=start;i<=n-cnt;i++){
        //随机选择一个
        v[i]=1;
        next_com(v,i+1,cnt-1);
        v[i]=0;//还原
    }
}

void solve(){
    //5*5的棋局共25  终局不同
    //题目要求解平局的情况 棋盘下满 那就是 13/12 白方13  黑方12 的棋盘情况
    //一共就5+5+2 12种连成5颗棋子的情况 平局那一定是总情况-白方胜  不可能是黑方胜
    //白方胜就是 (13,12) 最后一子
    //不可能这两个情况里都有 经过分析可以知道 不同的白方胜的情况 黑子可以走的总数是不一样的 因此我们只能通过模拟来进行
    //黑子 白子 都不能取胜  因此我们需要模拟组合数的问题 
    //这里有两种组合顺序的遍历 一种是递归(需要占空间) 另一种则是借助字典序生成组合  无需空间复杂度
    int n,k;
    n=25;
    k=13;
    vector<int> v(n,0);
    //这种排列模拟 一定要头脑清晰去判断逻辑 
    //先给一个最小的 从小到大进行 000...111
    for(int i=n-k;i<n;i++) v[i]=1; 
    //next_com(v,0,k);
//    while(1){
//        int i=n-2;
//        while(i>=0&&v[i]>=v[i+1]) i--;
//        if(i>=0){
//            int j=n-1;
//            while(j>=0&&v[j]<=v[i]) j--;
//            swap(v[i],v[j]);
//            //要reverse的
//            reverse(v.begin()+i+1,v.end());
//            if(check(v)) ans++;
//        } else{
//            //已经是最大的了
//            break;
//        }
//    }
    //还有更好的原地借助字典序生成 也可以用现成的函数
    while(1){
        if(check(v)) ans++;
        if(!next_permutation(v.begin(),v.end())) break; 
    }
    cout<<ans<<endl;
    
    
}




signed main() {
    int t = 1;
    //    cin>>t;
    while (t--) {
        solve();
    }
}
0 回复 0 转发 1 喜欢 4 阅读
回复 (0)
默认 最新
暂无回复,快来抢沙发!