• 一双木棋——状压dp


    题目大意

    给定一个\(n \times m\)的棋盘,每个格子有两个权值\(a,b\),A想最大化分差B最小化,A先出手。某格子可以下棋当且仅当其上方全部被下满并且左边全部被下满,请问下满整个棋盘的分数是多少?

    \(n,m \leq 10\)

    思路

    模拟下的过程,发现它图形永远都是要给上三角形。用二进制来维护它的轮廓进行dp。

    \(f[S]\)表示按照规则的当前得分,转移的时候找“拐角”(01/10),在拐角处进行操作维护。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int N = 11;
    const int INF = 0x3f3f3f3f;
    
    int n,m;
    int a[N][N];
    int b[N][N];
    int f[1 << (2 * N - 1)];
    bool vis[1 << (2 * N - 1)];
    
    int dp (int sta,int x) {
    	if(sta == ((1 << n) - 1) << m) return 0;
    	if(vis[sta]) return f[sta];
    	vis[sta] = 1;
    	f[sta] = x ? -INF : INF;
    	int xx = n;
    	int yy = 0;
    	for(int i = 0;i < (n + m - 1);i ++) {
    		if((sta >> i) & 1) {
    			-- xx;
    		}else ++ yy;
    		if(((sta >> i) & 3) != 1) continue;//不是拐角
    		int new_sta = sta ^ (3 << i);
    		if(x) {
    			f[sta] = max(f[sta],dp(new_sta,x ^ 1) + a[xx][yy]);
    		}
    		else f[sta] = min(f[sta],dp(new_sta,x ^ 1) - b[xx][yy]);
    	}
    	return f[sta];
    }
    int main () {
    	cin >> n >> m;
    	for(int i = 0;i < n; i ++) {
    		for(int j = 0;j < m; j ++) {
    			cin >> a[i][j];
    		}
    	}
    	for(int i = 0;i < n; i ++) {
    		for(int j = 0;j < m; j ++) {
    			cin >> b[i][j];
    		}
    	}
    	int sta = (1 << n) - 1;
    	cout << dp(sta,1) << endl;
    	return 0;
    }
    
  • 相关阅读:
    第十二周总结
    第十一周课程总结
    第十周第十周课程总结
    第九周课程总结&实验报告(七)
    第八周课程总结&实验报告(六)
    第七周课程总结&实验报告(五)
    第六周&java实验报告四
    第五周课程总结&试验报告(三)
    学期总结
    十四周总结
  • 原文地址:https://www.cnblogs.com/Allorkiya/p/15890944.html
Copyright © 2020-2023  润新知