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

48 篇文章 0 订阅
3 篇文章 0 订阅

题目描述

菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。

落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。

棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作 A_{i,j}Ai,j 、 B_{i,j}Bi,j 。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的 A_{i,j}Ai,j 之和,牛牛的得分是所有有白棋的格子上的 B_{i,j}Bi,j 的和。

菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

输入输出格式

输入格式:

从文件chess.in 中读入数据。

输入第一行包含两个正整数n;m,保证n;m <= 10。

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第i 行中第j 个数表示 A_{i,j}Ai,j 。

接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第i 行中第j 个数表示 B_{i,j}Bi,j 。

输出格式:

输出到文件chess.out 中。

输出一个整数,表示菲菲的得分减去牛牛的得分的结果。

输入输出样例

输入样例#1:  复制
2 3
2 7 3
9 1 2
3 7 2
2 3 1
输出样例#1:  复制
2

说明

样例1说明:

棋盘如图所示,双方都采用最优策略时,棋局如下:

• 菲菲下在第1 行第1 列(这是第一步时唯一可以落子的格子);

• 牛牛下在第1 行第2 列;

• 菲菲下在第2 行第1 列;

• 牛牛下在第1 行第3 列;

• 菲菲下在第2 行第2 列;

• 牛牛下在第2 行第3 列(这是这一步时唯一可以落子的格子);

• 填满棋盘,游戏结束,盘面如下。

菲菲的得分为:2 + 9 + 1 = 12 ;牛牛的得分为:7 + 2 + 1 = 10 。

对于所有的测试数据,n;m <= 10 , A_{i,j}Ai,j ; B_{i,j}Bi,j <= 100000。

对于编号为奇数的测试点,保证所有的 B_{i,j}Bi,j = 0 。


题解:原谅我,我到现在还是不太明白题解。。

点击打开链接

简单题。考场上的我是傻逼。

状压DP是容易想到的思路。若ss是一个状态(先不管它是怎么压出来的),若ss状态中有偶数个格子有棋子(现在轮到A下了),f[s]f[s]表示该状态之后的A得分与B得分之差的最大值(因为A想让这个得分差尽可能大);反之,若ss中有奇数个格子有棋子,则轮到B下了,f[s]f[s]表示该状态之后的A得分与B得分之差的最小值

注意f[s]f[s]表示的是达到状态ss之后的落子造成的得分差,而不是之前的!因为两个人的决策显然是根据【未来】做出的,而不是过去……(所以应该倒着枚举ss

首先,一个格子能落棋子当且仅当:【棋盘左上角到该格子】这个矩形中,只有该格子是空的。

那么每时每刻,有棋子的格子和空格子的分布一定是形如这样的('#'代表有棋子,'-'代表无棋子)

######
###---
##----
##----
#-----

这启发了我们如何进行状压。
想象'#'与'-'的分界线,像这样:

      ___|
    _|
   |
  _|
_|

显然,分界线永远从左下走向右上,期间只【向左走】或【向上走】。

用0和1表示分界线的形状:1表示向上走一格,0表示向右走一格。

例如这个局面

####
###
###
#

它的分界线形状可以表示为01001101,是一个长为n+mn+m的01串。

那么把01串作为二进制数来表示状态。由于串中一定有且只有nn个1,所以总共有效的状态只有Cnn+mCn+mn个,但是直接2n+m2n+m枚举就可以通过了。

总结一下:由大到小枚举状态ss,每次把其中的一个10改为01(这样分界线向外弯了一格,设这格为(x,y)(x,y)),设新状态为tt,则若状态ss中'#'有偶数个,f[s]=max{f[t]+Ax,y}f[s]=max{f[t]+Ax,y},否则f[s]=min{f[t]Bx,y}

f[s]=min{f[t]−Bx,y}


代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#define enter putchar('\n')
#define space putchar(' ')
using namespace std;
typedef long long ll;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
    if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
    x = x * 10 + c - '0';
    if(op == 1) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 15, INF = 0x3f3f3f3f;
int n, m, a[N][N], b[N][N], f[1 << 20];

void debug(int x){
    for(int i = 0; i < (n + m); i++)
    putchar('0' + (x >> i & 1));
}

int main(){

    read(n), read(m);
    for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++)
        read(a[i][j]);
    for(int i = 1; i <= n; i++)
    for(int j = 1; j <= m; j++)
        read(b[i][j]);
    for(int s = (1 << (n + m)) - 1, fi = 1; s >= 0; s--){
    int cnt1 = 0, area = 0;
    for(int i = 0; i < n + m; i++)
        if(s >> i & 1) cnt1++;
    if(cnt1 != n) continue;
    if(fi){
        fi = 0;
        continue;
    }
    for(int i = 0, x = n + 1, y = 1; i < n + m; i++){
        if(s >> i & 1) x--;
        else if(y++ <= m) area += x - 1;
    }
    f[s] = (area & 1) ? INF : -INF;
    for(int i = 0, x = n + 1, y = 1; i < n + m; i++){
        if(s >> i & 1) x--;
        else{
        if(i && (s >> (i - 1) & 1)){
            if(area & 1) f[s] = min(f[s], f[s ^ (3 << (i - 1))] - b[x][y]);
            else f[s] = max(f[s], f[s ^ (3 << (i - 1))] + a[x][y]);
        }
        y++;
        }
    }
    }
    write(f[(1 << n) - 1]), enter;

    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值