題目
chnlich 入手了一個新的掃雷遊戲。在這個遊戲中,地圖是一個N*M 的矩陣,矩陣的每一個點有一個數字或沒有數字。若一個格子內有數字,它表示它周圍⑨個格子內(它自己和與它有公共點的8 個格子)的地雷個數。每個格子內最多只能有1 顆地雷。
然而只有這些信息,我們不能馬上得知哪些格子內有地雷。不過因爲chnlich 過於⑨,他覺得只要知道地雷最少可能的顆數,就能夠挖掉所有的雷。他希望你幫他寫一個程序,解決這個問題。當然,程序運行速度太慢或者錯誤可不行,如果1s 之內chnlich 還沒有得到他
想要的信息,chnlich 就會把你打成⑨。
這個我們可以搜索來做
加上一下優化:
1、最優化剪枝,就是答案比現在的雷數小就break
2、可行性剪枝:
a、通過先選取能被越多數字包含的地方來做,並且預處理出,後面被少數字包含的地方都放滿還缺幾個,來確定下界
b、通過每個數字周圍還缺多少個雷,取最大值作爲上界(指被相同數字包含的地方的個數上界)
3、由於我打戳了,加了個卡時,但其實前兩個就夠了
貼代碼
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define N 16
#define M 100000
using namespace std;
int n,m,t,ans,sum,Time;
char map[N][N];
int a[N][N],help[N],b[3],s[M],one[M],c[M],f[M][N];
struct node{
int v[N];
}d;
void init(){
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
scanf(" %c",&map[i][j]);
}
void ins(int x,int y){
static int xx,yy,z;
z=0;
for (int i=0;i<3;i++){
xx=b[i]+x;
if (xx&&xx<=n)
for (int j=0;j<3;j++){
yy=b[j]+y;
if (yy&&yy<=m)
if (a[xx][yy]!=-1)
z+=help[a[xx][yy]];
}
}
s[z]++;
}
bool cmp(int x,int y){
return one[x]>one[y];
}
void dfs(int x,int y){
static bool p;
int l,r;
if (!(--Time))return;
p=1;
if (ans<=y)return;
l=0;
for (int i=0;i<sum;i++)
l=max(l,d.v[i]);
if (l+y>=ans)return;
for (int i=0;i<sum;i++)
if (d.v[i]){
p=0;
break;
}
if (p){
ans=y;
return;
}
l=0;
r=s[c[x]];
for (int i=0;i<sum;i++)
if (c[x]&help[i])
l=max(l,d.v[i]-f[x+1][i]),r=min(r,d.v[i]);
for (int i=r;i>=l;i--){
for (int j=0;j<sum;j++)
if (c[x]&help[j])
d.v[j]-=i;
dfs(x+1,y+i);
if (!Time)return;
for (int j=0;j<sum;j++)
if (c[x]&help[j])
d.v[j]+=i;
}
}
void pre(){
ans=0;
sum=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++){
a[i][j]=-1;
if (map[i][j]!='*'&&map[i][j]!='.')
ans+=(d.v[sum]=map[i][j]-'0'),a[i][j]=sum++;
}
for (int i=0;i<help[sum];i++)s[i]=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++)
if (map[i][j]!='.')
ins(i,j);
for (int i=1;i<help[sum];i++)
c[i]=i;
sort(c+1,c+help[sum],cmp);
c[0]=0;
for (int i=1;i<help[sum];i++)
if (s[c[i]])c[++c[0]]=c[i];
for (int i=0;i<sum;i++)
f[c[0]+1][i]=0;
for (int i=c[0];i;i--)
for (int j=0;j<sum;j++)
if (c[i]&help[j])
f[i][j]=min(f[i+1][j]+s[c[i]],d.v[j]);
else
f[i][j]=f[i+1][j];
}
void work(){
dfs(1,0);
}
void predfs(int x,int y,int z){
one[x]=z++;
for (int i=y;i<N;i++)
predfs(x+help[i],i+1,z);
}
int main(){
scanf("%d %d",&n,&m);
b[0]=-1,b[2]=1;
help[0]=1;
for (int i=1;i<N;i++)
help[i]=help[i-1]+help[i-1];
predfs(0,0,0);
while (n||m){
init();
pre();
if (ans!=45)Time=30000;
else
Time=4800000;
work();
printf("%d\n",ans);
scanf("%d %d",&n,&m);
}
return 0;
}