zoukankan      html  css  js  c++  java
  • 【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提高组的压轴题,
    往往没有稳定复杂度的算法。
    此时就要考虑上述几种做法,进行取舍,以骗得最高的分数。
    大致上就是平衡时间效率正确性


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

  • 相关阅读:
    解决:Failed to update database: "APP_DATA\ASPNETDB.MDF" is readonly .
    MySQL DATE_ADD() 函数
    用struts上传图片,中文命名的图片无法显示
    hrbustoj 1291 点在凸多边形内
    按标签来查技术文章
    ClassLoader in Java
    [Python]标准库inspect
    IMEI修改(IMEI第十五位验证码的计算)
    游戏之巅:游戏背后的创业风云
    常用的网站后台编辑器
  • 原文地址:https://www.cnblogs.com/hiweibolu/p/6714863.html
Copyright © 2011-2022 走看看