noi省选 [九省联考2018]一双木棋题解(状压dp)

Posted david--lj

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了noi省选 [九省联考2018]一双木棋题解(状压dp)相关的知识,希望对你有一定的参考价值。

比浙江简单多了。。。。。。。。

题目转送:https://www.luogu.org/problemnew/show/P4363

分析:

我们注意到n和m都很小,考虑一下状压dp。

显然,棋子摆成的形状一定是凸包,所以,我们用一个数组h,h[i]表示第i行的棋子个数,一定有h[i]>=h[i+1]

我们发现,dp肯定是要倒着做,因为两方都考虑了最优决策。至于状压,我用了11进制+map

然后就很简单了

#include <bits/stdc++.h>
using namespace std;
#define _l long long
int n,m,a[20][20],b[20][20];
map<_l,int>dp;
set<_l>hh;
void unzip(int* arr,_l s){
    int pos=n;
    while(s){
        arr[pos--]=s%11;s/=11;
    }
}
_l zip(int *arr){
    _l w=1;
    _l res=0;
    for(int i=n;i>0;--i,w*=11)
    res+=arr[i]*w;
    return res;
}
void dfs(_l u){
    if(hh.count(u))return;
    int pos=n,sta[20],i;
    for(i=1;i<=n+1;++i)sta[i]=0;
    unzip(sta,u);
    int cnt=0;hh.insert(u);
    for(i=1;i<=n;++i)cnt+=sta[i];++cnt;
    if(cnt%2==1)dp[u]=1<<31;else dp[u]=1<<30;
    for(i=1;i<=n;++i){
        if(i==1 || (sta[i-1]>sta[i])){
            ++sta[i];if(sta[i]>m){
                --sta[i];continue;
            }
            _l k=zip(sta);
            dfs(k);
            if(cnt%2==1)dp[u]=max(dp[u],dp[k]+a[i][sta[i]]);
            else dp[u]=min(dp[u],dp[k]-b[i][sta[i]]);
            --sta[i];
        }
    }
}
int main(){//freopen("in.txt","r",stdin);//freopen("o1.txt","w",stdout);
    scanf("%d%d",&n,&m);
    int i,j;
    for(i=1;i<=n;++i)for(j=1;j<=m;++j)scanf("%d",&a[i][j]);
    for(i=1;i<=n;++i)for(j=1;j<=m;++j)scanf("%d",&b[i][j]);
    int arr[20];
    memset(arr,0,sizeof(arr));
    for(i=1;i<=n;++i)arr[i]=m;
    _l k=zip(arr);hh.insert(k);dp[k]=0;
    dfs(0);
    printf("%d",dp[0]);
}
/*
2 3
3 5 3 
4 4 5 
2 1 5 
5 2 3 
*/

 

以上是关于noi省选 [九省联考2018]一双木棋题解(状压dp)的主要内容,如果未能解决你的问题,请参考以下文章

[BZOJ5248] 2018九省联考 D1T1 一双木棋 | 博弈论 状压DP

BZOJ5248九省联考2018一双木棋(搜索,哈希)

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

[2018九省联考]一双木棋

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

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