题解 matrix

传送门

考场上只会\(O(2^{nm})\)的大力状压……

其实跟状压的例题几乎一模一样…………但还是没看出来
关键特征:每个按钮向上,下只能影响一层
也就是说一个格子只能被它上面一层/本层/下面一层点亮
而且最终每个格子都要被点亮
直接按层状压就好了,几乎就是例题的样子

至于正解复杂度,有个很显然的上界是\(O(n*2^{3m})\)
然而其中有一层枚举的是子集,肯定跑不满
stO @Yubai 说这个拿二项式定理能证出来这两层整体复杂度为\(O(3^m)\)
所以最终正解复杂度为\(O(n*3^m*2^m)\)

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, m;

namespace force{
	int mat, dp[1<<20], val[20][20], minn=INF;
	bool ma[20][20];
	const int dlt[][2]={{-1,0}, {0,-1}, {0,0}, {0,1}, {1,0}};
	inline int read2() {
		int ans=0; char c=getchar();
		while (!isdigit(c)) c=getchar();
		while (isdigit(c)) {ans<<=1, ans|=(c-'0'); c=getchar();}
		return ans;
	}
	void solve() {
		memset(dp, 127, sizeof(dp));
		for (int i=1; i<=n; ++i) mat<<=m, mat|=read2();
		//cout<<"mat: "<<bitset<10>(mat)<<endl;
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) val[i][j]=read(), minn=min(minn, val[i][j]);
		if (n==1 && m==1) {
			if (mat==(1<<(n*m))-1) {puts("0"); exit(0);}
			else printf("%d\n", minn);
		}
		int lim=1<<(n*m);
		dp[mat]=0;
		for (int s=0; s<lim; ++s) {
			if (dp[s]>=2139062143ll) continue;
			for (int i=1; i<=n; ++i) 
				for (int j=1; j<=m; ++j) {
					int t=s;
					//memset(ma, 0, sizeof(ma));
					for (int i2=n; i2; --i2) for (int j2=m; j2; --j2) ma[i2][j2]=bool(t&1), t>>=1;
					for (int k=0; k<5; ++k) ma[i+dlt[k][0]][j+dlt[k][1]]=1;
					t=0;
					for (int i2=1; i2<=n; ++i2) for (int j2=1; j2<=m; ++j2) t<<=1, t|=ma[i2][j2];
					//cout<<"t: "<<bitset<10>(t)<<endl;
					dp[t] = min(dp[t], dp[s]+val[i][j]);
				}
		}
		printf("%d\n", dp[lim-1]);
	}
}

namespace task1{
	unordered_map< ll, int > dp;
	unordered_map< ll, bool > vis;
	ll mat, s;
	queue<ll> q;
	bool ma[20][20];
	int val[20][20];
	const int dlt[][2]={{-1,0}, {0,-1}, {0,0}, {0,1}, {1,0}};
	inline int read2() {
		int ans=0; char c=getchar();
		while (!isdigit(c)) c=getchar();
		while (isdigit(c)) {ans<<=1, ans|=(c-'0'); c=getchar();}
		return ans;
	}
	void solve() {
		for (int i=1; i<=n; ++i) mat<<=m, mat|=read2();
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) val[i][j]=read();
		ll lim=1ll<<(n*m);
		dp[mat]=0; q.push(mat);
		while (q.size()) {
			s=q.front(); q.pop();
			vis[s]=0;
			for (int i=1; i<=n; ++i) 
				for (int j=1; j<=m; ++j) {
					ll t=s;
					for (int i2=n; i2; --i2) for (int j2=m; j2; --j2) ma[i2][j2]=bool(t&1), t>>=1;
					for (int k=0; k<5; ++k) ma[i+dlt[k][0]][j+dlt[k][1]]=1;
					t=0;
					for (int i2=1; i2<=n; ++i2) for (int j2=1; j2<=m; ++j2) t<<=1, t|=ma[i2][j2];
					//cout<<"t: "<<bitset<10>(t)<<endl;
					if (dp.find(t)!=dp.end()) {
						if (dp[t] > dp[s]+val[i][j]) {
							dp[t]=dp[s]+val[i][j];
							if (!vis[t]) {
								vis[t]=1;
								q.push(t);
							}
						}
					}
					else {
						dp[t]=dp[s]+val[i][j];
						q.push(t);
						vis[t]=1;
					}
				}
		}
		printf("%d\n", dp[lim-1]);
		exit(0);
	}
}	

namespace task{
	int dp[15][1<<10][1<<10];
	int val[15][1<<10], a[20][20];
	int mat[15];
	inline int read2() {
		int ans=0; char c=getchar();
		while (!isdigit(c)) c=getchar();
		while (isdigit(c)) {ans<<=1, ans|=(c-'0'); c=getchar();}
		return ans;
	}
	void solve() {
		memset(dp, 127, sizeof(dp));
		for (int i=1; i<=n; ++i) mat[i]=read2(); //, cout<<"mat"<<i<<": "<<bitset<5>(mat[i])<<endl;
		for (int i=1; i<=n; ++i) for (int j=1; j<=m; ++j) a[i][m-j+1]=read();
		int lim=1<<m;
		dp[0][0][0]=dp[0][lim-1][0]=0;
		for (int i=1; i<=n; ++i) 
			for (int s=1; s<lim; ++s) 
				for (int j=0; j<m; ++j) if (s&(1<<j)) 
					val[i][s] += a[i][j+1];
		for (int i=1; i<=n; ++i) 
			for (int j=0; j<lim; ++j) 
				for (int k=0; k<lim; ++k) if ((((k|(k<<1)|(k>>1))&(lim-1))&j)==((k|(k<<1)|(k>>1))&(lim-1))) 
					for (int u=0; u<lim; ++u) if ((j|u) == (lim-1)) 
						dp[i][k| ((u|(u<<1)|(u>>1))&(lim-1)) |mat[i]][u] = min(dp[i][k| ((u|(u<<1)|(u>>1))&(lim-1)) |mat[i]][u], dp[i-1][j][k]+val[i][u]); //, cout<<"tran: "<<i<<' '<<j<<' '<<k<<' '<<u<<' '<<bitset<5>(k| ((u|(u<<1)|(u>>1))&(lim-1)) |mat[i])<<' '<<bitset<5>(u)<<' '<<dp[i][k| ((u|(u<<1)|(u>>1))&(lim-1)) |mat[i]][u]<<" from "<<dp[i-1][j][k]<<' '<<val[i][u]<<endl;
		int ans=INF;
		for (int j=0; j<lim; ++j) ans=min(ans, dp[n][lim-1][j]);
		printf("%d\n", ans);
		exit(0);
	}
}

signed main()
{
	n=read(); m=read();
	//if (n<=4 && m<=4) force::solve();
	//else task1::solve();
	task::solve();
	
	return 0;
}
上一篇:题解 [ABC164E] Two Currencies


下一篇:暑期专题五 数位DP