1. 程式人生 > >World Final 2015 F. Keyboarding

World Final 2015 F. Keyboarding

Keyboarding

這是一道BFS(但用到了不少的SPFA的思想)的題,不過出題者十分毒瘤,有眾多坑點:

1、按下方向鍵時跳到下一個不同的字元,也就意味著游標可以“傳送”。

2、最後要列印一個回車,也就是“*”號。

3、選擇也是一次操作。

4、可能有多組資料。

5、因為同樣的字母會重複出現,所以如果BFS一個接一個的找時,第一個找到的未必是全域性最優的,如圖:

[樣例2][1]
[1]: https://cdn.luogu.org/upload/pic/40343.png
若是一個一個字元去BFS,那順序是(1)(2)(3)(4)(5)(6)(7),因為只有該方向有不同字元,游標才會移動,所以是不能從(2)到(11)的。而正確的路線是(1)(8)(9)(10)(6)(7)。所以從當前位置到下一個要列印的字元的最短路線不一定對列印後面的字元有利。

思路

1、先預處理每個點可以到的地方(最多4個),存入f陣列。

2、list陣列儲存每一個BFS到的點的x、y座標,所用的步數(dep)及從出發點到這個點的路徑中已列印的字元數(find)。

3、dep陣列表示地圖上每一個點作為一條路徑的終點可以列印多少字元。

4、(重點)如果從出發點到當前的點再到另一個點比出發點到那個點的原路徑所列印的字元更多,那麼更新那個點的find和dep值,也就是說以這個點為終點,有另一條路徑,並且路徑上列印的字元更多(類似SPFA)。反則不更新,因為已經被遍歷過的點所用步數肯定比當前的點的步數少。

5、如果當前點是下一個要列印的點,就find++,表示去尋找下一個字元。

6、找到最後一個字元後輸出步數並return 0;

Code

(因為我太弱了不會用佇列,只好有迴圈陣列)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
int dx[4]={-1,0,1,0};//四個方向
int dy[4]={0,-1,0,1};//四個方向
char s[110000],map[2100][2100];
int r,c,xx,yy,len,head,tail,dep[2100][2100];
struct node
{
    int x,y,dep,find;//x,y是座標,dep表示當前所用步數,find表示以當前點為終點的路徑中已列印的字元數
}list[1600000];
struct nodee
{
    int x[5],y[5],len;//x,y陣列表示可以去到的位置,len表示可前往的點的數量
    nodee()
    {
        len=0;
    }
}f[2100][2100];
void chuli()//預處理每個點能去到的地方,記錄在f數組裡
{
    for(int i=1;i<=r;i++)
    for(int j=1;j<=c;j++)
    for(int k=0;k<=3;k++)
    {
        xx=i;
        yy=j;
        while(map[i][j]==map[xx+dx[k]][yy+dy[k]]&&
        xx+dx[k]&&yy+dy[k])//一直迴圈直到下一個點是合法的或者到達邊界
        {
            xx+=dx[k];
            yy+=dy[k];
        }
        if(map[i][j]!=map[xx+dx[k]][yy+dy[k]]&&
        xx+dx[k]&&yy+dy[k])//如果下一個是點是合法的,就加入f陣列
        {
            f[i][j].len++;
            f[i][j].x[f[i][j].len]=xx+dx[k];
            f[i][j].y[f[i][j].len]=yy+dy[k];
        }
    }
    return ;
}
int main()
{
    while(scanf("%d%d",&r,&c)!=EOF)//多組資料
    {
        memset(f,0,sizeof(f));
        memset(dep,-1,sizeof(dep));
        memset(list,0,sizeof(list));
        memset(map,' ',sizeof(map));//初始化
        for(int i=1;i<=r;i++)
        scanf("%s",map[i]+1);
        scanf("%s",s+1);
        len=strlen(s+1);
        s[len+1]='*';
        len++;//最後要列印一個回車,也就是“*”號
        chuli();//預處理
        xx=yy=1;
        head=1;
        tail=2;
        list[1].x=xx;
        list[1].y=yy;
        list[1].dep=0;
        list[1].find=0;
        while(head!=tail)
        {
            if(map[list[head].x][list[head].y]==s[list[head].find+1])如果當前的點是下一個要列印的字元,就重新加入佇列同時把已打字元數加1
            {
                list[tail]=list[head];
                list[tail].dep++;
                list[tail].find++;
                if(list[tail].find==len)//如果所有數都列印了,就輸出並結束。
                {
                    printf("%d\n",list[head].dep+1);
                    break;
                }
                tail++;
            }
            for(int k=1;k<=f[list[head].x][list[head].y].len;k++)//逐個遍歷當前點可以去到的其他點
            {
                xx=f[list[head].x][list[head].y].x[k];
                yy=f[list[head].x][list[head].y].y[k];
                if(dep[xx][yy]<list[head].find)//這個dep陣列與結構體中的dep不同,dep陣列表示地圖中每一個點作為終點,路徑上最多能列印的字元數,如果這個點作為終點有可列印更多的字元,就更新它並加入佇列去更新更多的點
                {
                    list[tail].x=xx;
                    list[tail].y=yy;
                    list[tail].dep=list[head].dep+1;
                    list[tail].find=list[head].find;
                    dep[xx][yy]=list[head].find;
                    tail++;
                    if(tail>1500000)
                    tail=1;
                }
            }
            head++;
        }
    }
    return 0;
}

不要試圖抄襲、複製或是刪掉註釋後提交程式碼,就算我不把你打死,你也AC不了的。(因為我修改了一點小東西,嘿嘿嘿