中秋假期乱做

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

晚自修想了十几 min,胡了一个做法。

考虑它这个 \(n,m\) 很小,猜测可能上到指数级别。考虑先分析放的操作,不难发现几个性质。

  1. \((i,j)\) 能够放置,当且仅当 \((i,j)\) 为空,且 \((1,1),(i,j)\) 这个矩形除了 \((i,j)\) 都已放置。证明考虑要放置 \((i,j)\),则 \((x,j),(i,y)\) 都应该放置,而这些要放置则又是约束,可看成横竖 2 条直线的限制。

  2. 每个时刻的放置状态一定呈倒三角阶梯状。即对于每列的行数单调不增。证明考虑下性质 1 即可反证。

那么,这样暴力还是会有 \(10^{10}\) 状态数。

但是考虑 dp 转移,发现枚举拐点,然后再枚举每个拐点那一层代表的高度即可,状态数只有 \(\sum_{i} \binom{m}{i} \binom{n+1}{i}\) 种,即枚举每列平台个数,再枚举平台高度。

熄灯了

然后预处理下记忆化搜索即可。

#include <bits/stdc++.h>
#define ll long long
#define pb push_back
using namespace std;
const int N=(int)(1e6+5);
map<vector<int>,int>mp; 
vector<vector<int> >p;
vector<int>vec[12],vec2[13],tmp,v;
ll f[N];
bool vis[N];
int n,m,a[11][11],b[11][11],tot,ed;

ll dfs(int x) {
	if(x==ed) {
		return 0ll;
	}
	if(vis[x]) return f[x];
	bool fl=0; int res=0;
	for(int i:p[x]) {
		res+=i;
	}
	if(res&1) fl=1;
	else fl=0;
	for(int i=0;i<p[x].size();i++) {
		int qwq=p[x][i];
		if(p[x][i]+1>n) continue ;
		bool ok=1;
		for(int j=0;j<i;j++) {
			if(p[x][j]<p[x][i]+1) ok=0;
		}
		if(ok) {
			v.clear();
			for(int j=0;j<i;j++) v.pb(p[x][j]);
			v.pb(p[x][i]+1);
			for(int j=i+1;j<p[x].size();j++) v.pb(p[x][j]);
			int NEX=mp[v];
//			cout<<x<<" "<<f[x]<<" "<<qwq<<" : "<<i<<" "<<fl<<" "; 
			if(!fl) {
				f[x]=max(f[x],dfs(NEX)+a[qwq+1][i+1]);
			} else {
//				cout<<b[qwq+1][i+1]<<'\n';
				f[x]=min(f[x],dfs(NEX)-b[qwq+1][i+1]);
			}
		}
	}
//	cout<<x<<" "<<f[x]<<'\n';
	vis[x]=1;
	return f[x];
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	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=0;S<(1<<m);S++) {
		if(S&1) vec[__builtin_popcount(S)].pb(S);
	}
	for(int S=0;S<(1<<(n+1));S++) {
		vec2[__builtin_popcount(S)].pb(S);
	}
	tot=-1;
	for(int i=1;i<=m;i++) {
		for(int S:vec[i]) {
			for(int T:vec2[i]) {
				v.clear(); tmp.clear();
				for(int j=0;j<=n;j++) {
					if((T>>j)&1) tmp.pb(j);
				}
				int las=-1;
				for(int j=1;j<=m;j++) {
					if((S>>(j-1))&1) {
						v.pb(las=tmp.back());
						tmp.pop_back();
					} else {
						v.pb(las);
					}
				}
//				if(v.empty()) {
//					cout<<S<<' '<<T<<'\n';
//				}
				mp[v]=++tot; p.pb(v);
				bool fl=1; int res=0;
				for(int x:v) {
					res+=x;
					if(x!=n) fl=0;
				}
				if(res&1) f[tot]=(ll)(2e16);
				else f[tot]=-(ll)(2e16);
				if(fl) {
					ed=tot;
				}
//				cout<<tot<<'\n';
//				for(int x:v) cout<<x<<" ";
//				cout<<'\n';
			}
		}
	}
	cout<<dfs(0);
	return 0;
}
posted @ 2022-09-07 22:10  FxorG  阅读(24)  评论(0编辑  收藏  举报