【JZOJ4824】【NOIP2016提高A组集训第1场10.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提高组的压轴题,
往往没有稳定复杂度的算法。
此时就要考虑上述几种做法,进行取舍,以骗得最高的分数。
大致上就是平衡时间效率正确性


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

原文地址:https://www.cnblogs.com/hiweibolu/p/6714863.html