P4363 [九省联考2018]一双木棋chess

棋盘落子状态表示与转移思路
博客介绍棋盘落子思路,指出只能在轮廓线拐点处落子,棋盘状态可用n+m长度二进制数表示,转移是10变成01,还给出代码转载链接。

思路

容易发现只能在轮廓线的拐点处落子,所以棋盘的状态可以用一个n+m长度的二进制数表示
转移就是10变成01

代码

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
// #define int long long
using namespace std;
int A[20][20],B[20][20],n,m;
//map<int,int> M,vis;
int M[(1<<21)],vis[(1<<21)];
void print(int x){
    for(int i=n+m-1;i>=0;i--)
        printf("%d",(x>>i)&1);
    putchar('\n');
}
int dfs(int state,int which){//0 feifei 1 niuniu
//  print(state);
//  printf("which:%d\n",which);
//  getchar();
    if(vis[state]){
//      printf("req=%d\n",M[state]);
        return M[state];
    }
    vis[state]=true;
    int x=m,y=0;
    int mid;
    if(!which)
        mid=-0x3f3f3f3f3f3f3f3fLL;
    else
        mid=0x3f3f3f3f3f3f3f3fLL;
    for(int i=0;i<n+m;i++){
        if((i+1)<n+m&&((state>>i)&1)==0&&((state>>(i+1))&1)==1){
//          printf("y=%d x=%d\n",y+1,x);
            int to=state^(3<<i);
            if(!which)
                mid=max(mid,dfs(to,which^1)+A[y+1][x]);
            else
                mid=min(mid,dfs(to,which^1)-B[y+1][x]);
//          printf("mid=%d\n",mid);
        }
        if((state>>i)&1)
            y++;
        else
            x--;
    }
    M[state]=mid;
//  printf("req=%d\n",M[state]);
    return mid;
}
signed main(){
//  freopen("test.in","r",stdin);
    scanf("%d %d",&n,&m);
//  int t=clock();
    int st=(((1<<(n))-1)<<m),end=(1<<n)-1;
//  print(st);
//  print(end);
    vis[end]=true;
    M[end]=0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&A[i][j]);
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&B[i][j]);
        }
    }
    printf("%d\n",dfs(st,0));
//  printf("time=%lf\n",1.0*(clock()-t)/CLOCKS_PER_SEC);
    return 0;
}

转载于:https://www.cnblogs.com/dreagonm/p/10569255.html

# P4363 [九省联考 2018] 一双 chess ## 题目描述 菲菲和牛牛在一块 $n$ 行 $m$ 列的盘上下,菲菲执黑先手,牛牛执白后手。 局开始时,盘上没有任何子,两人轮流在格子上落子,直到填满盘时结束。 落子的规则是:一个格子可以落子当且仅当这个格子内没有子且这个格子的左侧及上方的所有格子内都有子。 盘的每个格子上,都写有两个非负整数,从上到下第 $i$ 行中从左到右第 $j$ 列的格子上的两个整数记作 $a_{i,j}$ 和 $b_{i,j}$。 在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑的格子上的 $a_{i,j}$ 之和,牛牛的得分是所有有白的格子上的 $b_{i,j}$ 的和。 菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何? ## 输入格式 第一行有两个整数,分别表示盘的行数 $n$ 和列数 $m$。 第 $2$ 到第 $(n + 1)$ 行,每行 $m$ 个整数,第 $(i + 1)$ 行的第 $j$ 个整数表示 $a_{i, j}$。 第 $(n + 2)$ 到第 $(2n + 1)$ 行,每行 $m$ 个整数,第 $(n + i + 1)$ 行的第 $j$ 个整数表示 $b_{i, j}$。 ## 输出格式 输出一行一个整数,表示菲菲的得分减去牛牛的得分的结果。 ## 输入输出样例 #1 ### 输入 #1 ``` 2 3 2 7 3 9 1 2 3 7 2 2 3 1 ``` ### 输出 #1 ``` 2 ``` ## 说明/提示 ### 样例 1 说明 ![](https://cdn.luogu.com.cn/upload/pic/16877.png) 盘如图所示,双方都采用最优策略时,局如下: - 菲菲下在第 $1$ 行第 $1$ 列(这是第一步时唯一可以落子的格子)。 - 牛牛下在第 $1$ 行第 $2$ 列。 - 菲菲下在第 $2$ 行第 $1$ 列。 - 牛牛下在第 $1$ 行第 $3$ 列。 - 菲菲下在第 $2$ 行第 $2$ 列。 - 牛牛下在第 $2$ 行第 $3$ 列(这是这一步时唯一可以落子的格子)。 - 填满盘,游戏结束。 盘面如下: ![](https://cdn.luogu.com.cn/upload/pic/16878.png) 菲菲的得分为 $2 + 9 + 1 = 12$,牛牛的得分为 $7 + 2 + 1 = 10$。 ### 数据规模与约定 各测试点信息如下表。 ![](https://cdn.luogu.com.cn/upload/pic/16879.png) - 对于编号为奇数的测试点,保证 $b_{i, j} = 0$。 - 对于全部的测试点,保证 $1 \leq n, m \leq 10$,$0 \leq a_{i, j}, b_{i, j} \leq 10^5$。 思路:先写m个0,然后看每一行,有几个子就在第几个0后面插入一个1。为什么WA了 ```cpp #include <iostream> using namespace std; const int INF = 0x3f3f3f3f; const int MAX_N = 15; int n, m, dp[1 << 20]; int a[MAX_N][MAX_N]; int b[MAX_N][MAX_N]; bool first(int s) { int f = 0; for (int j = 0, k = 0; k < n + m; k++) if (s >> k & 1) f ^= j; else j++; return (f & 1) ^ 1; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> a[i][j]; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) cin >> b[i][j]; for (int s = 1 << n; s <= (1 << n) - 1 << m - 1; s++) { if (__builtin_popcount(s) != n) continue; int f = first(s); dp[s] = f ? -INF : INF; bool flag = false; for (int i = 0, j = 0, k = 0; k < n + m; k++) if (s >> k & 1) { i++; if (flag) { int p = s ^ (1 << k) ^ (1 << k - 1); if (f) dp[s] = max(dp[s], dp[p] + a[i][j]); else dp[s] = min(dp[s], dp[p] - b[i][j]); flag = false; } } else j++, flag = true; } cout << dp[(1 << n) - 1 << m - 1] << '\n'; return 0; } ```
09-02
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值