蒟蒻の小窝公开赛 Round 2 T5 3D Cube 题解 [搜索剪枝]
3D Cube
Description:
小 Z 拿到了一个二维矩阵。底面可以看作一个 \(n \times m\) 的方格纸。
在每一个格子上,可以放任意个方块。由于有地心引力,方块不会悬空,且不会用胶水粘住。
他给出了这个三维矩阵的三视图,请你构造一个方块组,满足以下条件:
对于每一行、每一列,最多只有一个“峰”出现。特别地,底层矩阵的周围的高度被视为 \(0\)。如 12321
是一个“峰”,而 13231
有两个“峰”。
再举一些例子:
方块排列 | “峰”的数量 |
---|---|
\(\texttt{123}\) | 1 |
\(\texttt{212}\) | 2 |
\(\texttt{122221}\) | 1 |
\(\texttt{00011000}\) | 1 |
\(\texttt{10010101}\) | 4 |
求出需要最少方块的矩阵。如果无解,请输出 -1
。
Input:
第一行两个整数:\(n,m\)。
第二行 \(n\) 个整数:左视图,第 \(i\) 个整数表示左视图中第 \(i\) 列的方块个数。
第三行 \(m\) 个整数:主视图,第 \(j\) 个整数表示主视图中第 \(j\) 列的方块个数。
接下来 \(n\) 行,每行 \(m\) 个整数:俯视图,其中 0
表示没有方块,1
表示有方块。
Output:
如果无解,则输出 -1
。
否则输出共 \(n\) 行,每行 \(m\) 个非负整数,为构造的三维矩阵的俯视图,其中第 \(i\) 行第 \(j\) 个整数表示该位置上的方块个数。
请确保按照输出格式输出,否则可能导致 SPJ 返回 UKE 等结果。
Sample Input:
3 3
2 2 2
2 2 2
0 1 0
1 1 1
0 1 0
Sample Output:
0 2 0
2 2 2
0 2 0
Hint:
本题各部分测试点采用捆绑测试。
对于 \(20\%\) 的数据:给出输入文件,程序打表输出答案,见附件 easy.zip
。
对于 \(100\%\) 的数据:\(n\times m\leq25\)。在俯视图中每个格子上最多有 \(7\times10^8\) 个方块,且在俯视图中 1
的个数 \(\leq20\)。
时间限制: \(2s\)
空间限制: \(256M\)
附件下载
easy.zip \(844B\)
题目分析:
我们发现俯视图中\(1\)的个数是非常少的(毕竟是手造的数据)。于是我们就很容易想到搜索。
我们经过思考可以发现,一个非零位置可以放的数字只有5种选择——
1.为了保证答案最小,可以放个 \(1\) ;
2.为了使得左视图中某一列的方块数为给定值,我们可以在这个位置上放这一给定值的方块;
3.为了使得主视图中某一列的方块数为给定值,我们可以在这个位置上放这一给定值的方块;
4.为了保证某一列只有一个“峰”出现,而我们又希望总方块数尽可能的小,我们可以在这个位置放上和同一列上一行方块数一样的方块。
5.为了保证某一行只有一个“峰”出现,而我们又希望总方块数尽可能的小,我们可以在这个位置放上和同一行上一列方块数一样的方块。
于是复杂度就变成 \(5^{20}\)。
当然虽然实际跑不满,但还是不足以过了此题。因此我们考虑各种玄学剪枝——
1.当前放的方块总数已经超过一种合法方案的总数,直接结束。
2.对于每一行边做边判断是否每行都满足左视图的要求,并且这一行只有一个”峰“,详情见代码;
3.对于每一列边做边判断这一列是否只有一个”峰“,详情见代码;
通过这些优化剪枝,我们成功水过了此题!
坑点:主视图和左视图中可能某一列为 \(0\) ,需要特殊判断!
代码如下(马蜂很丑,不喜勿喷)——
#include<bits/stdc++.h>
#define LL long long
#define inf 2147483647777
using namespace std;
LL anss=inf,sum;
int n,m,tot,sumx,sumy,ans[30][30],ok1[30],ok2[30],a[30],b[30],xx[30],v[30][30];bool vis[30],ok[30][30];
inline void dfs(int x,int y){
if(sum>=anss) return;if(y>m){y=1,x++;if(!a[x]&&x<=n) sumx++;if(sumx<x-1) return;}
if(x==1&&!b[y]) sumy++;if(x==n+1){
if(sumy<m) return;bool flgg=1;for(register int j=1;j<=m&&flgg;j++){
bool flg=0;for(register int i=1;i<=n&&flgg;i++){
int now=i;while(v[now][j]==v[now+1][j]&&now<n) now++;
if(v[i][j]>v[i-1][j]&&v[i][j]>v[now+1][j]) if(flg) flgg=0;else flg=1;i=now;
}
}
if(!flgg) return;anss=sum;for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++) ans[i][j]=v[i][j];return;
}
if(!ok[x][y]){if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);return;}if(a[x]<=b[y]){
if(!ok1[x]) ok1[x]=x*(n+1)+y,sumx++;if(a[x]==b[y]) if(!ok2[y]) ok2[y]=x*(n+1)+y,sumy++;
sum+=a[x],v[x][y]=a[x];if(v[x][y]<=v[x][y-1]||!vis[x]) if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);
sum-=a[x];if(ok1[x]==x*(n+1)+y) ok1[x]=0,sumx--;if(ok2[y]==x*(n+1)+y) ok2[y]=0,sumy--;
}
else{
if(!ok2[y]) ok2[y]=x*(n+1)+y,sumy++;sum+=b[y],v[x][y]=b[y];
if(v[x][y]<=v[x][y-1]||!vis[x]) if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);
sum-=b[y];if(ok1[x]==x*(n+1)+y) ok1[x]=0,sumx--;if(ok2[y]==x*(n+1)+y) ok2[y]=0,sumy--;
}
for(register int i=1;i<=1;i++) if(xx[i]<a[x]&&xx[i]<b[y]){
if(xx[i]==a[x]) if(!ok1[x]) ok1[x]=x*(n+1)+y,sumx++;if(xx[i]==b[y]) if(!ok2[y]) ok2[y]=x*(n+1)+y,sumy++;
sum+=xx[i],v[x][y]=xx[i];if(v[x][y]<=v[x][y-1]||!vis[x]) if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);
sum-=xx[i];if(ok1[x]==x*(n+1)+y) ok1[x]=0,sumx--;if(ok2[y]==x*(n+1)+y) ok2[y]=0,sumy--;
}
if(ok[x-1][y]&&v[x-1][y]<a[x]&&v[x-1][y]<b[y]&&v[x-1][y]!=1){
if(v[x-1][y]==a[x]) if(!ok1[x]) ok1[x]=x*(n+1)+y,sumx++;if(v[x-1][y]==b[y]) if(!ok2[y]) ok2[y]=x*(n+1)+y,sumy++;
sum+=v[x-1][y],v[x][y]=v[x-1][y];if(v[x][y]<=v[x][y-1]||!vis[x]) if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);
sum-=v[x-1][y];if(ok1[x]==x*(n+1)+y) ok1[x]=0,sumx--;if(ok2[y]==x*(n+1)+y) ok2[y]=0,sumy--;
}
if(ok[x][y-1]&&v[x][y-1]<a[x]&&v[x][y-1]<b[y]&&v[x][y-1]!=1){
if(v[x][y-1]==a[x]) if(!ok1[x]) ok1[x]=x*(n+1)+y,sumx++;if(v[x][y-1]==b[y]) if(!ok2[y]) ok2[y]=x*(n+1)+y,sumy++;
sum+=v[x][y-1],v[x][y]=v[x][y-1];if(v[x][y]<=v[x][y-1]||!vis[x]) if(v[x][y]<v[x][y-1]){vis[x]=1,dfs(x,y+1),vis[x]=0;}else dfs(x,y+1);
sum-=v[x][y-1];if(ok1[x]==x*(n+1)+y) ok1[x]=0,sumx--;if(ok2[y]==x*(n+1)+y) ok2[y]=0,sumy--;
}
}
inline int read(){
int ret=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-') f=-f;ch=getchar();}
while(isdigit(ch)) ret=(ret<<1)+(ret<<3)+ch-'0',ch=getchar();
return ret*f;
}
int main(){
n=read(),m=read();xx[++tot]=1;
for(register int i=1;i<=n;i++) a[i]=read(),xx[++tot]=a[i];
for(register int j=1;j<=m;j++) b[j]=read(),xx[++tot]=b[j];
sort(xx+1,xx+tot+1);int xxx=tot;tot=1;for(register int i=2;i<=xxx;i++) if(xx[i]!=xx[i-1]) xx[++tot]=xx[i];
if(!xx[1]){for(register int i=1;i<tot;i++) xx[i]=xx[i+1];tot--;}
for(register int i=1;i<=n;i++) for(register int j=1;j<=m;j++) ok[i][j]=read();
dfs(0,m+1);if(anss==inf) cout<<-1;else for(register int i=1;i<=n;puts(""),i++)
for(register int j=1;j<=m;j++) cout<<ans[i][j]<<' ';return 0;
}