传送门
考场上只会\(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; }