P2493 [SDOI2011] 贪食蛇AC题解

本人 6年级 耗时0.2个月luoguP2493 [SDOI2011] 贪食蛇AC

接下来由我为大家讲解

首先有一个结论:

当前最强的蛇吃了最弱的蛇之后,如果没有变成最弱的蛇,他一定会选择吃!

证明:

假设当前最强的蛇叫石老板。

如果下一条最强的蛇如果依旧是石老板,那肯定不吃白不吃;

如果下一条最强蛇不是石老板,此时最强的蛇没有原先强,最弱的蛇也没原先弱,吃掉后肯定比石老板要弱。也就是说,当前最强的蛇吃了之后,如果会死,也会死在石老板前面。那么这样一来,这条蛇会想尽办法不死,从而石老板也一定能不死。

有了这个结论,一部分蛇可以放心大胆地吃了,但是问题来了,如果吃了之后变成最弱的蛇了,到底选择吃不吃呢?

稍微往后推一推就明白了:

当前最强蛇记为石老板,下一条最强蛇记为喵老板。石老板进食后变成最弱的蛇了,如果喵老板进食后不是最弱的蛇,他就会选择吃(根据开头的结论),这样石老板就凉了,所以石老板当初的选择一定是不吃。

如果喵老板进食后依旧是最弱的蛇,那就会考虑下一条最强蛇的情况,起名为汪老板。同样分两种情况:如果汪老板进食后不是最弱的蛇,那他就会选择吃,这样喵老板就凉了,所以他当初会选择不吃,这样石老板就不会死,那么石老板当初就会选择吃。如果汪老板进食后变成了最弱的蛇,那就再考虑下一条蛇………………

这个问题就变成了一个递归的问题了,直到某条蛇吃了之后不是最弱的蛇或者只能下两条蛇为止。这样,最后一条蛇会选择吃,倒数第二条蛇为了保命会选择不吃,倒数第三条蛇可以放心大胆的吃,倒数第四条蛇会保命选择不吃,倒数第五条蛇可以放心吃………………

这样,石老板选择吃不吃,就和最后一条蛇之间的奇偶性相关了。并且石老板选择不吃,游戏结束,石老板选择吃,游戏也会在下一轮结束(因为喵老板会选择不吃)。

到目前为止,这个题目很清晰了,只需模拟两个阶段即可:

阶段一:所有最强蛇进食后都不是最弱蛇,放心大胆吃!

阶段二:所有最强蛇进食后都是最弱蛇,直到有条蛇可以放心吃为止(吃了后不是最弱或者只剩两条)

阶段一结束时,游戏就基本结束了(根据阶段二的奇偶性看能不能再吃一次)

接下来送出AC代码 代码为C++14 运行

感谢大家的收看

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
using namespace std;
const int maxr=17,maxc=17,maxq=15*15*1440*16*8,maxS=1<<11;
const int  dx[]={0,0,-1,1};
const int  dy[]={-1,1,0,0};
const char Dir[]={'L','R','U','D'};
int snakePos0[4][2];
int map[maxr][maxc],foodMap[maxr][maxc];
int stat[maxS][4],foodStat[maxS][4];
int q[maxq],pred[maxq];
unsigned char visit[1<<23];
char path[1000];
int ansTime,ansLink,foodCnt,pathLength,r,c;
int expand(int stat,int len,int k){
    int tmp=stat;
    len+=4;
    k^=1;
    int x=dx[k],y=dy[k];
    for(int i=2;i<len;++i){
        x+=dx[tmp&3];
        y+=dy[tmp&3];
        tmp>>=2;
        if(!x && !y) return -1;
    }
    return (stat<<2 & ((1<<(len+len-2))-1)) | k;
}
void calcStat(){
    //calculate the state of snake when it moves.
    memset(stat,0xff,sizeof stat);
    memset(foodStat,0xff,sizeof foodStat);
    int head=0,tail=1;
    int visit[1<<14]={0};
    for(int i=3;i>0;--i){
        int k=0;
        while(snakePos0[i-1][0]+dx[k]!=snakePos0[i][0]||snakePos0[i-1][1]+dy[k]!=snakePos0[i][1])
            ++k;
        q[1]=q[1]<<2|k;
    }
    q[1]<<=2;//头部没有“方向“,这两位是存储长度的。
    visit[q[1]]=1;
    while(head<tail){
        int s0=q[++head];
        int len0=s0&3;
        int snakeStat0=s0>>2;
        for(int k=0;k<4;++k){
            int snakeStat1=expand(snakeStat0,len0,k);
            if(snakeStat1>=0){
                int s1=(snakeStat1 ) << 2 |len0;
                if(!visit[s1]){
                    q[++tail]=s1;
                    visit[s1]=tail;
                }
                stat[head][k]=visit[s1];
            }
        }
        //grow
        for(int k=0;k<4;++k){
            int snakeStat1=expand(snakeStat0,len0+1,k);
            if(snakeStat1>=0){
                if(len0<3){
                    int s1=(snakeStat1 ) << 2 |(len0+1);
                    if(!visit[s1]){//好像0xfff不加也行。。。
                        q[++tail]=s1;
                        visit[s1]=tail;
                    }
                    foodStat[head][k]=visit[s1];
                }
                else{
                    foodStat[head][k]=0;
                    //到达长度8,即到达目标状态
                }
            }
        }
    }
//  printf("%d %d",head,tail);//tail=1200!!!
}
#define mkStat(x,y,ss,fs,wt) (((x)<<22)|((y)<<18)|((ss)<<7)|((fs)<<3)|(wt))
void bfs(){//这里的 ss是上面calcStat中队列的下标
//  memset(q,0,sizeof q);
//  memset(visit,0,sizeof visit);
    int head=0,tail=1;
    q[1]=mkStat(snakePos0[0][0],snakePos0[0][1],1,(1<<foodCnt)-1,0);
    visit[q[1]>>3]|=1<<0;
    while(head<tail){
        int s0=q[++head];
        int x0          = s0 >> 22 & 0xf,
            y0          = s0 >> 18 & 0xf,
            snakeStat0  = s0 >>  7 & 0x7ff,
            foodStat0   = s0 >>  3 & 0xf,
            waitTime0   = s0 & 7;
            if(!foodStat0 && !waitTime0){
                ansLink=head;
    //          printf("Anslink=%d\n",ansLink);
                break;
            }
            if(waitTime0){
                int s1=mkStat(x0,y0,snakeStat0,foodStat0,waitTime0-1);
                if(!(visit[s1>>3] & 1<<(waitTime0-1))){
                    visit[s1>>3]|=1<<(waitTime0-1);
                    q[++tail]=s1;
                    pred[tail]=head;
                }
            }
            else{
                for(int k=0;k<4;++k){
                    int x1=x0+dx[k],
                        y1=y0+dy[k],
                        snakeStat1=stat[snakeStat0][k],
                        foodStat1=foodStat0,
                        waitTime1=abs(map[x1][y1]-map[x0][y0]);
                        if(!map[x1][y1]) continue;
                        if(foodMap[x1][y1]&&(foodStat1& 1<< (foodMap[x1][y1]-1))){
                            foodStat1^=1<<(foodMap[x1][y1]-1);
                            snakeStat1=foodStat[snakeStat0][k];
                        }
                        else snakeStat1=stat[snakeStat0][k];
                        if(snakeStat1>=0){                      
                            int s1=mkStat(x1,y1,snakeStat1,foodStat1,waitTime1);

                            if(!(visit[s1>>3] & 1<< waitTime1)){//不要减一!!不要减一!!
                                visit[s1>>3]|=1<< waitTime1;
                                q[++tail]=s1;
                                pred[tail]=head;
                            }
                        }
                }
            }
    }
//  printf("%d\n",tail);
}
inline void input(){
    scanf("%d%d",&r,&c);
    char str[20];
    for(int i=1;i<=r;++i){
        scanf("%s",str);
        for(int j=0;j<c;++j){
            map[i][j+1]=str[j]-48;
        }
    }
    for(int i=0;i<4;++i)
        scanf("%d%d",&snakePos0[i][0],&snakePos0[i][1]);
    int x,y;
    scanf("%d",&foodCnt);
    for(int i=1;i<=foodCnt;++i){
        scanf("%d%d",&x,&y);
        foodMap[x][y]=i;
    }
}
inline void calcAns(){
    bfs();
    int p1=ansLink;
    int x0,y0,x1,y1,wt;
    x1=q[p1]>>22&0xf;
    y1=q[p1]>>18&0xf;
    while(p1>1){
        int p0=pred[p1];
        x0=q[p0]>>22 & 0xf;
        y0=q[p0]>>18 & 0xf;
//      wt=q[p0]&7;
        ansTime++;
/*      if(!wt){
            int k=0;
            while(x0+dx[k]!=x1||y0+dy[k]!=y1)
                ++k;
            path[pathLength++]=Dir[k];
        }
*/      x1=x0,y1=y0;
        p1=p0;
    }
//  reverse(path,path+pathLength);
}
inline void output(){
    if(!ansLink)
        puts("No solution.");
    else{
        printf("%d\n",ansTime);
//      for(int i=0;i<pathLength;++i)
//          putchar(path[i]);
//      putchar('\n');
    }
}
int main(){
    input();
    calcStat();
    calcAns();
    output();
    return 0;
}
  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值