题解 Board Game

传送门

我脑残了判存在只会暴力
其实压位之后可以判子集
那么就有一个 \(O(nm2^{nm})\) 的暴力了:
\(f_s\) 为状态为 \(s\) 是否包含了给定图案
\(dp_s\) 为一个人放棋子后状态为 \(s\) 这个人是否必胜
转移就按定义转移即可,可以有 50 pts
然后注意转移的时候枚举 lowbit 就可以有 75 pts
然后指令集优化一下就可以有 100 pts
那么这个题就讲完了
好的那么考虑优化
发现 \(n\times m=27\)

  • 只有高维前缀和的可行性 DP 可以压位从 \(O(n2^n)\) 优化到 \(O(2^n)\)
    具体地,将最低 \(6\) 位压进一个 ull 里
    一共 \(2^6=64\) 个状态对应这个 ull 的 64 个位
    那么转移时高 \(n-6\) 位暴力转移
    \(6\) 位可以预处理一些东西来优化
    若是 \(s\to s|(1\ll i)\),那么可以预处理 \(sub_s\),其中若 \(t\)\(s\) 的恰好少一个 1 的子集则 \(sub_s\) 的第 \(t\) 位为 1
    那么从低到高转移,某一个位置 \(s\) 值为 1 的条件就是 \(f\&sub_s\) 不为 0
    这样的复杂度是 \(O(2^{n-6}\times 2^6)=O(2^n)\)

对于这道题,dp 数组的转移可以从高到低考虑
那么 dp[s]&=~dp[s|(1<<i)]
注意要先处理前 \(n-6\) 位的转移再考虑后 6 位的转移,否则转移后 6 位的时候无法保证使用的状态是合法的
举个例子:
\(f_{000001}\) 应为 0,但在未转移高位前为 1
那么 \(f_{000000}\) 这样转移就会出问题

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
#define ull unsigned long long
//#define int long long

int n, m, k, h, w;
char s[N];

namespace force{
	short any_mp[1<<12], rec[1<<12];
	int a[12][12], tot;
	// unordered_map<ull, bool> mp;
	pair<pair<int, int>, int> ban[N];
	int ban2[12][12][1050], cnt[12][12];
	const ull base1=13131, base2=131;
	// ull hashing(int u, int v, int n, int m, int a[30][30]) {
	// 	ull ans=0;
	// 	for (int i=u; i<=n; ++i) {
	// 		ull t=0;
	// 		for (int j=v; j<=m; ++j) t=t*base1+a[i][j];
	// 		ans=ans*base2+t;
	// 	}
	// 	return ans;
	// }
	// bool any(int s) {
	// 	int a[30][30];
	// 	decode(s, a);
	// 	for (int i=1; i<=n; ++i)
	// 		for (int j=1; j<=m; ++j)
	// 			for (int k=i; k<=n; ++k)
	// 				for (int l=j; j<=m; ++j)
	// 					if (mp.find(hashing(i, j, k, l, a))!=mp.end())
	// 						return 1;
	// 	return 0;
	// }
	pair<pair<int, int>, int> encode(int n, int m, int a[12][12]) {
		int ans=0, tot=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j,++tot)
				if (a[i][j]) ans|=1<<tot;
		return {{n, m}, ans};
	}
	int encode(int a[12][12]) {
		int ans=0, tot=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j,++tot)
				if (a[i][j]) ans|=1<<tot;
		return ans;
	}
	void decode(int s, int a[12][12]) {
		int tot=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j,++tot)
				if (s&(1<<tot)) a[i][j]=1;
				else a[i][j]=0;
	}
	bool any(int s) {
		if (any_mp[s]!=-1) return any_mp[s];
		int a[12][12];
		decode(s, a);
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j)
				for (int k=i; k<=n; ++k)
					for (int l=j; l<=m; ++l) {
						int u=n-i+1, v=m-j+1, t=encode(u, v, a).sec;
						for (int now=1; now<=cnt[u][v]; ++now)
							if ((ban2[u][v][now]&t)==ban2[u][v][now]) 
								return any_mp[s]=1;
					}
		return any_mp[s]=0;
	}
	bool dfs(int s) {  // return true if the first one always wins.
		int a[12][12];
		if (rec[s]!=-1) return rec[s];
		decode(s, a);
		// cout<<"---a---"<<endl;
		// for (int i=1; i<=n; ++i) {for (int j=1; j<=m; ++j) cout<<a[i][j]<<' '; cout<<endl;}
		bool any_lose=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j) if (!a[i][j]) {
				a[i][j]=1;
				int s=encode(a);
				if (any(s)) return rec[s]=1;
				if (!dfs(s)) {any_lose=1; goto jump;}
				a[i][j]=0;
			}
		jump: ;
		return rec[s]=any_lose;
	}
	void solve() {
		// cout<<double(sizeof(ban2)+sizeof(any_mp)+sizeof(ban)+sizeof(ban2))/1000/1000<<endl;
		memset(any_mp, -1, sizeof(any_mp));
		memset(rec, -1, sizeof(rec));
		for (int i=1; i<=k; ++i) {
			scanf("%d%d", &h, &w);
			for (int j=1; j<=h; ++j) {
				scanf("%s", s+1);
				for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
			}
			ban[++tot]=encode(h, w, a);
		}
		sort(ban+1, ban+tot+1);
		tot=unique(ban+1, ban+tot+1)-ban-1;
		for (int i=1; i<=tot; ++i) {
			int u=ban[i].fir.fir, v=ban[i].fir.sec;
			ban2[u][v][++cnt[u][v]]=ban[i].sec;
		}
		puts(dfs(0)?"Alice":"Bob");
	}
}

namespace task1{
	int f[30][30];
	bool dfs(int a, int b) {
		if (a<0||b<0) return 1;
		if (f[a][b]!=-1) return f[a][b];
		if (a<=1) return f[a][b]=1;
		else return f[a][b]=(dfs(a-1, b)&dfs(a, b-1))?0:1;
	}
	void solve() {
		memset(f, -1, sizeof(f));
		int cnt0=0, cnt1=0;
		scanf("%d%d", &h, &w);
		for (int i=1; i<=h; ++i) {
			scanf("%s", s+1);
			for (int j=1; j<=w; ++j)
				if (s[j]=='1') ++cnt1;
				else ++cnt0;
		}
		puts(dfs(cnt1, cnt0)?"Alice":"Bob");
	}
}

namespace task2{
	int a[30][30];
	bool f[1<<24], dp[1<<24];
	int encode(int a[30][30]) {
		int ans=0, tot=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j,++tot)
				if (a[i][j]) ans|=1<<tot;
		return ans;
	}
	void solve() {
		for (int i=1; i<=k; ++i) {
			memset(a, 0, sizeof(a));
			scanf("%d%d", &h, &w);
			for (int j=1; j<=h; ++j) {
				scanf("%s", s+1);
				for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
			}
			int s=encode(a);
			// cout<<"s: "<<bitset<20>(s)<<endl;
			for (int j=h; j<=n; ++j)
				for (int k=w; k<=m; ++k)
					f[s<<(k-w)<<(m*(j-h))]=1;
		}
		int lim=1<<(n*m);
		for (int s=0; s<lim; ++s) dp[s]=1;
		for (int s=0; s<lim; ++s) if (f[s])
			for (int i=0; i<(n*m); ++i) if (!(s&(1<<i)))
				f[s|(1<<i)]|=f[s];
		for (int s=lim-1; ~s; --s) if (dp[s]|f[s]) {
			dp[s]|=f[s];
			for (int i=0; i<(n*m); ++i) if (s&(1<<i))
				dp[s^(1<<i)]=0;
		}
		puts(dp[0]?"Bob":"Alice");
	}
}

namespace task{
	int a[30][30];
	ull f[1<<23], dp[1<<23], vis[1<<23], sub[1<<6], sup[1<<6];
	bool f2[1<<23], dp2[1<<23];
	int encode(int a[30][30]) {
		int ans=0, tot=0;
		for (int i=1; i<=n; ++i)
			for (int j=1; j<=m; ++j,++tot)
				if (a[i][j]) ans|=1<<tot;
		return ans;
	}
	void solve() {
		for (int i=1; i<=k; ++i) {
			memset(a, 0, sizeof(a));
			scanf("%d%d", &h, &w);
			for (int j=1; j<=h; ++j) {
				scanf("%s", s+1);
				for (int k=1; k<=w; ++k) a[j][k]=(s[k]=='1');
			}
			int s=encode(a);
			// cout<<"s: "<<bitset<20>(s)<<endl;
			for (int j=h; j<=n; ++j)
				for (int k=w; k<=m; ++k) {
					int t=s<<(k-w)<<(m*(j-h));
					f[t>>6]|=1llu<<(t&63);
				}
			for (int j=h; j<=n; ++j)
				for (int k=w; k<=m; ++k)
					f2[s<<(k-w)<<(m*(j-h))]=1;
		}
		int len=max(n*m-6, 0), lim=1<<len, lim2=1<<6;
		for (int s=0; s<lim2; ++s)
			for (int i=0; i<6; ++i) if (!(s&(1<<i)))
				sub[s|(1<<i)]|=1llu<<s, sup[s]|=1llu<<(s|(1<<i));
		for (int s=0; s<lim; ++s) {
			for (int i=0; i<lim2; ++i)
				if (f[s]&sub[i]) f[s]|=1llu<<i;
			// for (int i=0; i<len; ++i) if (!(s&(1<<i)))
			// 	f[s|(1<<i)]|=f[s];
			for (int t=(~s)&(lim-1); t; t-=t&-t)
				f[s|(t&-t)]|=f[s];
		}
		for (int s=lim-1; ~s; --s) {
			dp[s]=~dp[s];
			// for (int i=0; i<len; ++i) if (!(s&(1<<i)))
			// 	dp[s]&=~dp[s|(1<<i)];
			for (int t=(~s)&(lim-1); t; t-=t&-t)
				dp[s]&=~dp[s|(t&-t)];
			dp[s]|=f[s];
			for (int i=lim2-1; ~i; --i)
				if (((~dp[s])&sup[i])!=sup[i])
					dp[s]&=~(1llu<<i), dp[s]|=f[s];
			// if (!s && i==34)
			// if (!s) cout<<(dp[s]&(1ll<<34)?1:0)<<' '; cout<<endl;
			// cout<<bitset<5>(dp[s])<<endl;
			// cout<<bitset<5>(sup[1])<<endl;
			// if (!s) cout<<(dp[s]&(1ll<<0)?1:0)<<endl;
			// for (int i=(1<<6)-1; ~i; --i) {
			// 	cout<<"dp: "<<bitset<7>((s<<1)|i)<<' '<<(dp[s]&(1ll<<i)?1:0)<<endl;
			// }
		}
		// int lim2=1<<(n*m);
		// for (int s=0; s<lim2; ++s) dp2[s]=1;
		// for (int s=0; s<lim2; ++s) if (f2[s])
		// 	for (int i=0; i<(n*m); ++i) if (!(s&(1<<i)))
		// 		f2[s|(1<<i)]|=f2[s];
		// for (int s=0; s<lim2; ++s)
		// 	if (f2[s]) assert(f[s>>6]&(1llu<<(s&63)));
		// 	else assert(!(f[s>>6]&(1llu<<(s&63))));
		// for (int s=lim2-1; ~s; --s) if (dp2[s]|f2[s]) {
		// 	dp2[s]|=f2[s];
		// 	for (int i=0; i<(n*m); ++i) if (s&(1<<i))
		// 		dp2[s^(1<<i)]=0;
		// }
		// // for (int s=lim2-1; ~s; --s) cout<<"dp2: "<<bitset<7>(s)<<' '<<dp2[s]<<endl;
		// for (int s=0; s<lim2; ++s)
		// 	if (dp2[s]) assert(dp[s>>6]&(1llu<<(s&63)));
		// 	else assert(!(dp[s>>6]&(1llu<<(s&63))));
		puts((dp[0]&1)?"Bob":"Alice");
	}
}

signed main()
{
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);

	scanf("%d%d%d", &n, &m, &k);
	// force::solve();
	// task1::solve();
	// if (n*m>10) task1::solve();
	// else force::solve();
	// task2::solve();
	task::solve();

	return 0;
}
posted @ 2022-03-22 17:17  Administrator-09  阅读(1)  评论(0编辑  收藏  举报