2018.12.22【NOIP提高組】模擬B組 JZOJ 3511 cza的蛋糕
阿新 • • 發佈:2018-12-22
大意
給定一個 的矩陣,其中一些點已經被覆蓋,現在要用 的長方形去覆蓋這個矩陣(可以90度翻轉),問放到不能再放的最少長方形放置數。
資料範圍:
對於30%的資料
對於100%的資料
思路
10%玄學
程式碼1
#include<algorithm>
#include<cstdio>
using namespace std;int n,m;
signed main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
scanf("%d%d",&n,&m);
printf("%d",max(n,m));
}
30%暴搜
程式碼2
#include<cstdio>
#include<iostream>
using namespace std;int n,m,ans=0x3f3f3f3f;
char c;
bool ok[71][9];
inline char Getchar()
{
static char buf[100000],*p1=buf+100000,*pend=buf+100000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,100000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline bool check()
{
for(register int i=1;i<=n;i++)
for(register int j=1;j<=m;j++)
if(!ok[i][j])
if(!ok[i+1][j]||!ok[i][j+1]) return false;
return true;
}
inline void dfs(register int now,register int x,register int y)
//線上的 過程 深度優先搜尋 (記錄整型 當前使用的長方形個數,記錄整型 當前是第幾行,記錄整型 當前是第幾個)
{
if(now>=ans) return;//最優性剪枝
if(x==n&&y==m)
{
if(check())ans=now;//儲存答案
return;
}
if(ok[x][y])//已被覆蓋
{
if(y==m) dfs(now,x+1,1);//下一行
else dfs(now,x,y+1);//下一個
return;
}
ok[x][y]=true;//填充
if(!ok[x][y+1]&&y<m)//後面那格可以填
{
ok[x][y+1]=true;//填
dfs(now+1,x,y+1);
ok[x][y+1]=false;//回溯
}
if(!ok[x+1][y]&&x<n)//下面那格可以填
{
ok[x+1][y]=true;//填
if(y==m) dfs(now+1,x+1,1);
else dfs(now+1,x,y+1);//判斷是否填到邊界
ok[x+1][y]=false;
}
ok[x][y]=false;
if(ok[x-1][y]||ok[x][y-1])//可以空
{
if(y==m) dfs(now,x+1,1);//直接空掉這格
else dfs(now,x,y+1);
}
}
signed main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
cin>>n>>m;
for(register int i=1;i<=n;i++)
{
ok[i][0]=ok[i][m+1]=1;
for(register int j=1;j<=m;j++)
{
cin>>c;
ok[0][j]=ok[n+1][j]=1;//周圍不能再填
if(c=='*') ok[i][j]=1;//標記
}
}
dfs(0,1,1);//搜尋
cout<<ans;
}
我們發現這樣的程式碼沒有記憶化,於是乎我們就想到了記憶化,但是這樣由於狀態很多沒法表示,於是就可以用狀態壓縮每一行,然後進行狀壓 (記憶化搜尋)
AC程式碼
#include<cstdio>
#include<cstring>
#include<iostream>
#define ri register int
using namespace std;
int p[11],f[71][131][131],n,m;
char c;
int i,j,l,a[72],s,ans=0x3f3f3f3f;
inline void cge(ri k,ri x, ri y,ri z,ri t)
//線上的 過程 修改(當前填補的列數,上一行的狀態,這一行的狀態,下一行的狀態,當前步數)
{
if(k>0&&((x&p[k-1])==0)&&((y&p[k-1])==0)) return;//若出現縱的兩個空位,不合法,退出
if(k>1&&((y&p[k-1])==0)&&((y&p[k-2])==0)) return;//若出現橫的兩個空位,不合法,退出
if(k==m)//搜尋完畢
{
f[i][y][z]=min(f[i][y][z],f[i-1][j][l]+t);//狀態轉移
return;
}
cge(k+1,x,y,z,t);//這一格不填
if(((z&p[k])==0)&&((y&p[k])==0))//能填縱的
cge(k+1,x,y|p[k],z|p[k],t+1);//這一行的和下一行的都要修改
if(k<m-1&&((y&p[k])==0)&&((y&p[k+1])==0))//能填橫的
cge(k+2,x,y|p[k]|p[k+1],z,t+1);//修改這一行的兩個值
}
signed main()
{
freopen("cake.in","r",stdin);
freopen("cake.out","w",stdout);
cin>>n>>m;
for(i=1;i<=n;i++)
{
int x=0;
for(j=1;j<=m;j++)
{
cin>>c;
x<<=1;
if(c=='*') x++;
}
a[i]=x;
}
memset(f,0x3f3f3f3f,sizeof(f));
p[0]=1;
for(i=1;i<10;i++) p[i]=p[i-1]<<1;
s=p[m]-1;
f[0][s][a[1]]=0;
for(i=1;i<=n;i++)
for(j=0;j<=s;j++)
for(l=0;l<=s;l++)
if(f[i-1][j][l]<0x3f3f3f3f)
cge(0,j,l,a[i+1],0);
for(i=0;i<=s;i++)
ans=min(ans,f[n][i][0]);
cout<<ans;
}