BZOJ 5248 [九省联考2018]一双木棋chess 轮廓线DP || Hash

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/88849909

title

BZOJ 5248
LUOGU 4363
题目描述

菲菲和牛牛在一块n 行m 列的棋盘上下棋,菲菲执黑棋先手,牛牛执白棋后手。 棋局开始时,棋盘上没有任何棋子,两人轮流在格子上落子,直到填满棋盘时结束。
落子的规则是:一个格子可以落子当且仅当这个格子内没有棋子且这个格子的左侧及上方的所有格子内都有棋子。
棋盘的每个格子上,都写有两个非负整数,从上到下第i 行中从左到右第j 列的格 子上的两个整数记作 A i , j B i , j A_{i,j}、B_{i,j} 。在游戏结束后,菲菲和牛牛会分别计算自己的得分:菲菲的得分是所有有黑棋的格子上的 A i , j A_{i,j} 之和,牛牛的得分是所有有白棋的格子上的 B i , j B_{i,j} 的和。
菲菲和牛牛都希望,自己的得分减去对方的得分得到的结果最大。现在他们想知道,在给定的棋盘上,如果双方都采用最优策略且知道对方会采用最优策略,那么,最终的结果如何。

输入输出格式
输入格式:

从文件chess. in 中读入数据。
输入第一行包含两个正整数n;m,保证n;m <= 10。
接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第一个非负整数:其中第i 行中第j 个数表示 A i , j A_{i,j}
接下来n 行,每行m 个非负整数,按从上到下从左到右的顺序描述每个格子上的 第二个非负整数:其中第i 行中第j 个数表示 B i , j B_{i,j}

输出格式:

输出到文件chess.out 中。
输出一个整数,表示菲菲的得分减去牛牛的得分的结果。

输入输出样例
输入样例#1:

2 3
2 7 3
9 1 2
3 7 2
2 3 1

输出样例#1:

2

analysis

首先,由于一个位置能落子,当且仅当上面和左边都没有空位,根据这个依赖关系的传递性,不难发现一个位置能落子,当且仅当左上角的矩形内部也只有自己一个空位。

那么,可以证明,任何时候,棋盘上的棋子都是一个连续且单调的的左上三角形,所有我们可以用一个轮廓线来表示三角形的右下边界,这样就可以表达棋盘的状态了

不妨用 1 1 表示竖着的轮廓边, 0 0 表示横着的轮廓边。
从左下角开始,任何一个轮廓线都可以表达为长度为 n + m n+m ,且拥有 n n 1 1 m m 0 0 01 01

比如下图中,初始状态就是 00011 00011
在这里插入图片描述
显然第一步棋是唯一的,下完以后的轮廓线可以表示为 00101 00101
如果这个时候走第一行第二列,那么状态可以表示为 01001 01001
如果这个时候走第二行第一列,那么状态可以表示为 00110 00110
然后,可以发现,状态的转移就是把其中一个 1 1 向左挪一个位置即可

本质就是 01 &gt; 1001 &gt; 10 01-&gt;1001−&gt;10
所有找到这些位置就可以转移啦

然后发现转移的顺序不太明显。。。记忆化搜索。。。

S S 表示一条从左下到右上的轮廓线,令 f [ S ] f[S] 表示这个轮廓线的状态距离游戏结束还能得多少分,可以得到边界条件 f [ 11 1100 00 ] = 0 f[11\cdots1100\cdots00] = 0 ,最终的答案便是 f [ 00 0011 11 ] f[00\cdots0011\cdots11]
转移的时候看看是谁在下棋,顺着轮廓线定位到当前落子的位置,然后取个 m i n , m a x min,max 就好了,代码较短,复杂度是 ( n + m n ) \binom{n+m}{n} 的,所以跑的比较快。——来自Anoxiacxy

code

01 01 串轮廓线 D P DP

#include <bits/stdc++.h>
using namespace std;
const int N=10;
const int inf=0x7fffffff;
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:* fs++;}
template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getc();
	while (!isdigit(ch) && ch^'-') ch=getc();
	if (ch=='-') f=-1, ch=getc();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getc();
	x*=f;
}
template<typename T>inline void put(T x)
{
    if (!x) putchar('0');
    if (x<0) putchar('-'), x=-x;
    T num=0,ch[20];
    while (x) ch[num++]=x%10, x/=10;
    while (num) putchar(ch[--num]+48);
}
inline int Max(register int x,register int y)
{
    return x>y?x:y;
}
inline int Min(register int x,register int y)
{
    return x<y?x:y;
}
int n,m,a[N][N],b[N][N];
int f[1<<(N<<1)];
bool vis[1<<(N<<1)];
inline int dfs(register int now,register int who)
{
    if (vis[now])
        return f[now];
    f[now]=who?-inf:inf;
    int x=n,y=0;
    for (register int i=0; i<n+m-1; ++i)
    {
        if (now>>i&1)
            --x;
        else
            ++y;
        if ((now>>i&3)!=1)
            continue;
        int nxt=now^(3<<i);
        if (who)
            f[now]=Max(f[now],dfs(nxt,who^1)+a[x][y]);
        else
            f[now]=Min(f[now],dfs(nxt,who^1)-b[x][y]);
    }
    vis[now]=true;
    return f[now];
}
int main()
{
	read(n);read(m);
    for (register int i=0;i<n;++i)
        for (register int j=0;j<m;++j)
            read(a[i][j]);
    for (register int i=0;i<n;++i)
        for (register int j=0;j<m;++j)
            read(b[i][j]);
    vis[((1<<n)-1)<<m]=true;
    put(dfs((1<<n)-1,1));
    return 0;
}

H a s h Hash ,用 m a p map

#include<bits/stdc++.h>
using namespace std;
const long long inf=0x7fffffff;
char buf[1<<15],*fs,*ft;
inline char getc(){return (fs==ft&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),fs==ft))?0:* fs++;}
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getc();
    while (!isdigit(ch) && ch^'-') ch=getc();
    if (ch=='-') f=-1, ch=getc();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getc();
    x*=f;
}
map<long long,long long>now,vis;
long long a[101][101],b[101][101],num[21],n,m;
inline int Max(int x,int y)
{
    return x>y?x:y;
}
inline int Min(int x,int y)
{
    return x<y?x:y;
}
inline long long ask(long long x)
{
    if (vis[x])
        return now[x];
    vis[x]=1;
    long long sum=0;
    for (register long long i=0; i<n; ++i)
        sum+=x/num[i]%11;
    if (sum&1)
    {
        long long y=inf;
        if (x%11<m)
            y=Min(y,ask(x+1)-b[1][x%11+1]);
        for (register long long i=1; i<n; ++i)
            if (x/num[i]%11 < x/num[i-1]%11)
                y=Min(y,ask(x+num[i])-b[i+1][x/num[i]%11+1]);
        return now[x]=y;
    }
    else
    {
        long long y=-inf;
        if (x%11<m)
            y=Max(y,ask(x+1)+a[1][x%11+1]);
        for (register long long i=1; i<n; ++i)
            if (x/num[i]%11 < x/num[i-1]%11)
                y=Max(y,ask(x+num[i])+a[i+1][x/num[i]%11+1]);
        return now[x]=y;
    }
}
int main()
{
    read(n);read(m);
    num[0]=1;
    for (register long long i=1; i<=n; ++i)
        num[i]=num[i-1]*11;
    long long end=0;
    for (register long long i=0; i<n; ++i)
        end+=num[i]*m;
    for (register long long i=1; i<=n; ++i)
        for (register long long j=1; j<=m; ++j)
            read(a[i][j]);
    for (register long long i=1; i<=n; ++i)
        for (register long long j=1; j<=m; ++j)
            read(b[i][j]);
    vis[end]=1;
    printf("%lld\n",ask(0));
    return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/88849909