GDOI2016模擬8.14掃雷遊戲

題目
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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章