1. 程式人生 > >洛谷 P2905 [USACO08OPEN]農場危機Crisis on the Farm(惡心的DP)

洛谷 P2905 [USACO08OPEN]農場危機Crisis on the Farm(惡心的DP)

names 方向 online 危機 blog sequence get 行動 要求

P2905 [USACO08OPEN]農場危機Crisis on the Farm

1605: [Usaco2008 Open]Crisis on the Farm 牧場危機

題目描述

約翰和他的奶牛組建了一只樂隊“後街奶牛”,現在他們正在牧場裏排練.奶牛們分成一堆 一堆,共1000)堆.每一堆裏,30只奶牛一只踩在另一只的背上,疊成一座牛塔.牧場 裏還有M(1 < M < 1000)個高高的草垛.

作為出色的指揮家,約翰可以通過口哨指揮奶牛們移動.他的口哨有四個音,分別能使所有 的牛塔向東南西北四個方向移動一格.

每一次,當一個牛塔到達了一個草垛所在的格子,牛塔最上方的奶牛就會跳到草垛上,而且 不再下來,而其他奶牛仍然呈塔狀站在草垛所在的格子裏.當牛塔只剩一只奶牛時,這只奶牛也 會跳到草垛上.

突然,約翰大驚失色:原來鄰家的奶缸爆炸了!滾滾而下的牛奶正朝著約翰的牧場沖來,不久就要將牧場淹沒.約翰必須馬上行動,用口哨聲挽救奶牛們的生命.他要指揮奶牛盡量多地跳 上草操,草操上的奶牛將不會被淹死.

約翰還有K次吹口哨的機會.那他最多還能救多少奶牛呢?請計算最多能挽救的奶牛數,以及 達到這個數目約翰需要吹的口哨調子序列.序列用E,W,S,N表示東西南北.如果有多種序列能達到 要求,輸出作為字符串最小的.

輸入輸出格式

輸入格式:

  • Line 1: Three space-separated integers: N, M, and K

  • Lines 2..N+1: Line i+1 describes the X,Y location of a stack of 30 cows using two space-separated integers: X_i and Y_i

  • Lines N+2..N+M+1: Line i+N+1 describes the X,Y location of a haystack using two space-separated integers: X_i and Y_i

輸出格式:

  • Line 1: A single integer that is the most number of cows that can be saved.

  • Line 2: K characters, the lexicographically least sequence of commands FJ should issue to maximize the number of cows saved.

輸入輸出樣例

輸入樣例#1:
3 6 3 
3 4 
6 2 
5 7 
8 2 
9 2 
6 4 
5 4 
6 7 
8 7 
輸出樣例#1:
6 
EEE 

說明

Use the ‘east‘ whistle three times, at which point the milk floods the area. Each haystack ends up saving 1 cow.

思路:見代碼。

錯因:輸出格式錯誤。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1500
using namespace std;
int n,m,K,ans;
int dx[4]={1,0,0,-1};
int dy[4]={0,1,-1,0};
char step[40][64][64];
char C[4]={W,S,N,E};
int cnt[64][64],f[40][64][64]; //f[k][i][j]記錄走k步,縱向移動了i-31步,橫向移動了j-31步,所能拯救的最多的牛的數量。
int cawx[MAXN],cawy[MAXN],grassx[MAXN],grassy[MAXN];//記錄牛和草垛的橫縱坐標.
int main(){
    scanf("%d%d%d",&n,&m,&K);
    for(int i=1;i<=n;i++)
        scanf("%d%d",&cawx[i],&cawy[i]);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&grassx[i],&grassy[i]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++){
            int cx=cawx[i]-grassx[j];    //第i頭牛移動到第j個草垛,縱向最少所走的距離.    
            int cy=cawy[i]-grassy[j];    //第i頭牛移動到第j個草垛,橫向最少所走的距離.
            if(abs(cx)<=30&&abs(cy)<=30)//因為k<=30,所以當有一個方向的距離大於30,就不用考慮了,因為一定不可能走到. 
                cnt[cx+31][cy+31]++;    //否則cnt記錄走縱向i步橫向走j步所能拯救的牛的數量. 
        }
    for(int k=0;k<=K;k++)
        for(int i=0;i<=62;i++)
            for(int j=0;j<=62;j++){
                f[k][i][j]=-0x3f3f3f3f;
                step[k][i][j]=Z;
            }
    f[0][31][31]=0;    //賦初值,最開始時所能拯救的牛的數量為0. 
    //這裏要理解,因為他可以向上下左右走,為了防止負坐標的出現,我們把一開始時的原點坐標當做(31,31).
    for(int k=1;k<=K;k++)
        for(int i=1;i<=61;i++)
            for(int j=1;j<=61;j++)
                f[k][i][j]=cnt[i][j]+max(max(f[k-1][i-1][j],f[k-1][i+1][j]),max(f[k-1][i][j-1],f[k-1][i][j+1]));
                //這個狀態轉移方程就不用解釋了,還是很容易理解的. 
    for(int i=1;i<=61;i++)
        for(int j=1;j<=61;j++)
            ans=max(ans,f[K][i][j]);
    for(int i=1;i<=61;i++)
        for(int j=1;j<=61;j++)
            if(ans==f[K][i][j])
                step[K][i][j]=A;    //如果為縱向走i步橫向走j步是一種可行的走法,記錄以方便求字典序最小. 
    for(int k=K-1;k>=0;k--)
        for(int i=1;i<=61;i++)
            for(int j=1;j<=61;j++)
                for(int l=0;l<4;l++)
                    if(f[k][i][j]+cnt[i+dx[l]][j+dy[l]]==f[k+1][i+dx[l]][j+dy[l]]&&step[k+1][i+dx[l]][j+dy[l]]<Z)
                        step[k][i][j]=C[l];    //倒序找出所有可能的走法. 
    cout<<ans<<endl;
    int i=31,j=31;
    for(int k=0;k<K;k++){
        cout<<step[k][i][j];
        if(step[k][i][j]==E)    i--;    //找字典序最小的. 
        else if(step[k][i][j]==W)    i++;
        else if(step[k][i][j]==S)    j++;
        else if(step[k][i][j]==N)    j--;    
    }
}

洛谷 P2905 [USACO08OPEN]農場危機Crisis on the Farm(惡心的DP)