1 条题解
-
0
注意一个题意理解上的问题:每一个窗口只能被一个胶带纸贴着,比如下面这份输入数据:
5 5 .#.#. ##### .#.#. ##### .#.#.Mirko 显然可以用四个胶带全部贴上,但是因为他只能“将一横排或一纵列连续的窗口合上”,而被贴过的窗口已经是合上的,所以在贴新胶带的时候变成不连续的了,因此应该输出 而不是 。
用记忆化搜索做的轮廓线 dp,设 为考虑到第 行第 列,前面的 个空是否选择竖着涂的状态是 ,剩下最少需要几个胶带。具体的思路已经在注释里了。
#include<bits/stdc++.h> using namespace std; int n,m,M; int b[1005][15]; int dp[1005][15][2000]; int nxti(int i,int j){ if(j<m)return i; return i+1; } int nxtj(int i,int j){ if(j<m)return j+1; return 1; } int lsti(int i,int j){ if(j>1)return i; return i-1; } int lstj(int i,int j){ if(j>1)return j-1; return m; } int dfs(int i,int j,int S){//考虑到第 i 行第 j 列,前面的 m 个空竖着涂的状态是 S,剩下最少要几个胶带 S&=M; if(i>n)return 0; if(dp[i][j][S]!=-1)return dp[i][j][S]; int ans=114514; int ni=nxti(i,j),nj=nxtj(i,j); int li=lsti(i,j),lj=lstj(i,j); if(!b[i][j])return dp[i][j][S]=dfs(ni,nj,S<<1); //先看能不能竖着涂 //如果上面有竖着涂的,可以选择继续竖着涂 if(S&(1<<(m-1)))ans=min(ans,dfs(ni,nj,(S<<1)|1)); //如果上面没有竖着涂的,可以自己开始竖着涂 else ans=min(ans,1+dfs(ni,nj,(S<<1)|1)); //再看能不能横着涂,要看前一个有没有选择横着涂 if(i!=li)ans=min(ans,1+dfs(ni,nj,S<<1));//如果是一行开头左边啥都没有只能新开一个横着涂 else if(b[li][lj]&&(S&1)==0)ans=min(ans,dfs(ni,nj,S<<1));//前一个需要涂但没有竖着涂,那就是横着的了 else ans=min(ans,1+dfs(ni,nj,S<<1));//不能接到左边,只能自己开始横着涂 return dp[i][j][S]=ans; } int main(){ ios::sync_with_stdio(0);cin.tie(0); cin>>n>>m; M=(1<<m)-1; for(int i=0;i<=n;i++){ for(int j=0;j<(1<<m);j++){ for(int k=0;k<=m;k++)dp[i][k][j]=-1; } } for(int i=1;i<=n;i++){ string s;cin>>s; for(int j=0;j<m;j++){ if(s[j]=='#'){ b[i][j+1]=1; } } } cout<<dfs(1,1,0); return 0; }
- 1
信息
- ID
- 10840
- 时间
- 1000ms
- 内存
- 512MiB
- 难度
- 10
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者