题解 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;
}