【JZOJ4824】【NOIP2016提高A组集训第1场10.29】配对游戏

来源:互联网 发布:mysql存储过程模板 编辑:程序博客网 时间:2024/05/05 06:29

题目描述

流行的跳棋游戏是在一个有m*n个方格的长方形棋盘上玩的。棋盘起初全部被动物或障碍物占满了。在一个方格中,‘X’表示一个障碍物,一个‘0’~‘9’的个位数字表示一个不同种类的动物,相同的个位数字表示相同种类的动物。一对动物只有当它们属于同一种类时才可以被消去。消去之后,他们所占的方格就成为空方格,直到游戏结束。要消去一对动物的前提条件是:这对候选动物所在的方格必须相邻,或它们之间存在一条通路。棋盘上一个方格只和其上下左右的方格相邻。一条通路是由一串相邻的空方格组成。路的长度则是通路中空方格的数目。你要输出可被消去的动物的最多对数,以及在此操作过程中,最小的通路长度总和。

数据范围

N,M<=5

解法

暴力+剪枝

正确性可以保证,时间效率较低;
可以考虑牺牲点正确性以提高时间效率。

A*

正确性和时间效率取决于估价函数。

带策略的暴力

正确性勉强,时间效率卡常数;

贪心

正确性无法保证,时间效率高;
所以可以考虑牺牲时间效率,多次求解求最优。

代码

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>using namespace std;const char* fin="pair.in";const char* fout="pair.out";const int inf=0x7fffffff;const int maxn=13,maxm=maxn*maxn;const int h[4][2]={{1,0},{-1,0},{0,1},{0,-1}};int n,m,i,j,k,maxx,ans1,ans2,interesting=5,hx,hy,ans3,ans4;char a[maxn][maxn];bool bz[maxn][maxn];int b[maxm][2],head,tail,c[6];int f[maxn][maxn];int heavy(int x,int y){    return max(abs(x-hx),abs(y-hy));}void add(int x,int y,int z,int awful){    if (z>=f[x][y] || (!bz[x][y] && awful==0) || a[x][y]=='X') return ;    b[++tail][0]=x;    b[tail][1]=y;    f[x][y]=z;}int getdis(int x,int y,int u,int v){    int i,j,k,ans=inf;    memset(f,127,sizeof(f));    head=tail=0;    add(x,y,0,1);    while (head++<tail){        for (i=0;i<4;i++){            j=b[head][0]+h[i][0];            k=b[head][1]+h[i][1];            if (j>0 && j<=n && k>0 && k<=m)                if (j==u && k==v) ans=min(f[b[head][0]][b[head][1]],ans);                else add(j,k,f[b[head][0]][b[head][1]]+1,0);        }    }    return ans;}void work(int x,int y){    int i,j,k,minx=10000,mnid[2],mminx=inf;    for (i=1;i<=n;i++)        for (j=1;j<=m;j++)            if (!bz[i][j] && a[i][j]==a[x][y]){                if (x==i && y==j) continue;                k=getdis(x,y,i,j);                if (k<minx || k==minx && heavy(i,j)<mminx){                    minx=k;                    mminx=heavy(i,j);                    mnid[0]=i;                    mnid[1]=j;                }            }    if (minx==10000) return;    else{        /*ans1++;        ans2+=minx;        bz[x][y]=true;        bz[mnid[0]][mnid[1]]=true;*/        if (c[1]>minx){            c[1]=minx;            c[2]=x;            c[3]=y;            c[4]=mnid[0];            c[5]=mnid[1];        }    }}void solve(int v){    int i,j,k;    while (1){        c[1]=10000;        for (i=v;i<=n-v+1;i++) if (!bz[i][m-v+1] && a[i][m-v+1]!='X') work(i,m-v+1);        for (i=m-v+1;i>=v;i--) if (!bz[i][v] && a[i][v]!='X') work(i,v);        for (i=v;i<=n-v+1;i++) if (!bz[n-v+1][i] && a[n-v+1][i]!='X') work(n-v+1,i);        for (i=m-v+1;i>=v;i--) if (!bz[v][i] && a[v][i]!='X') work(v,i);        if (c[1]<10000){            bz[c[2]][c[3]]=true;            bz[c[4]][c[5]]=true;            ans1++;            ans2+=c[1];        }else break;    }}int main(){    freopen(fin,"r",stdin);    freopen(fout,"w",stdout);    scanf("%d%d",&n,&m);    for (i=1;i<=n;i++) scanf("%s",a[i]+1);    maxx=min((n+1)/2,(m+1)/2);    for (hx=1;hx<=n;hx++)        for (hy=1;hy<=m;hy++){            memset(bz,0,sizeof(bz));            ans1=ans2=0;            for (i=1;i<=maxx;i++){                for (j=i;j;j--){                    solve(j);                }            }            if (ans1>ans3 || ans1==ans3 && ans2<ans4){                ans3=ans1;                ans4=ans2;            }        }    for (hx=1;hx<=n;hx++)        for (hy=1;hy<=m;hy++){            memset(bz,0,sizeof(bz));            ans1=ans2=0;            for (i=maxx;i;i--){                for (j=i;j;j--){                    solve(j);                }            }            if (ans1>ans3 || ans1==ans3 && ans2<ans4){                ans3=ans1;                ans4=ans2;            }        }    for (hx=1;hx<=n;hx++)        for (hy=1;hy<=m;hy++){            memset(bz,0,sizeof(bz));            ans1=ans2=0;            for (i=maxx;i;i--){                for (j=1;j<=maxx;j++){                    solve(j);                }            }            if (ans1>ans3 || ans1==ans3 && ans2<ans4){                ans3=ans1;                ans4=ans2;            }        }    for (hx=1;hx<=n;hx++)        for (hy=1;hy<=m;hy++){            memset(bz,0,sizeof(bz));            ans1=ans2=0;            for (i=1;i<=maxx;i++){                for (j=i;j;j--){                    solve(j);                }            }            if (ans1>ans3 || ans1==ans3 && ans2<ans4){                ans3=ans1;                ans4=ans2;            }        }    printf("%d %d",ans3,ans4);    return 0;}

启发

这种题常出现在NOIP提高组的压轴题,
往往没有稳定复杂度的算法。
此时就要考虑上述几种做法,进行取舍,以骗得最高的分数。
大致上就是平衡时间效率正确性


此外还有考虑编程复杂度的问题,以我的观点:
贪心的编程复杂度最简单,也最容易调试;
因为贪心的每一次操作具有后效性。

1 0
原创粉丝点击