1. 程式人生 > >[NOIP2009]普及組T4 道路遊戲

[NOIP2009]普及組T4 道路遊戲

題意:在環形公路上,給定n個工廠製造機器人的花費以及n條道路每個時刻的金幣數,一個時刻必須有也只能有一個機器人在公路上收集金幣,求m時刻後能收集的最多金幣。這題題意很明確,顯然是dp,這裡我們講的是貪心的做法,另外還有單調佇列的做法。

我們要儘可能多地收集金幣,就得考慮第i時刻買不買機器人。所以我們要分清什麼情況下買機器人,什麼情況下不買機器人。
先介紹一下程式碼中要用到的陣列及變數。

dp[i][j] 表示i時刻在j工廠能收集到的最多金幣
step[i][j] 表示在i時刻在j工廠的機器人已經走的步數
pre[i] 表示i工廠前一個工廠編號,當i>1時,pre[i]=i-1;當i=1時,pre[i]=n
mx 表示當前時刻能得到的最多金幣
pastmx 表示前一個時刻能得到的最多金幣

買機器人的情況:
    (1)step[i-1][pre[j]]=p,即表示上個機器人已經走了p步,機器人不能再走了;(2)pastmx-cost[pre[j]]≥dp[i-1][pre[j]],即表示在i-1時刻的某個工廠收集到金幣減去j工廠前一個工廠製造的花費仍大於等於i-1時刻到達j工廠前一個工廠收集到的金幣數,也就是說pastmx-dp[i-1][pre[j]]足以支付pre[j]的製造費用,此時買機器人能走的步數比之前多,顯然更優。

不買機器人的情況:
    不買機器人就是除去買機器人的情況,step加1,能獲得的金幣增加

#include<stdio.h>
#include<algorithm> #include<iostream> #define oo 2000000000 #define M 1005 using namespace std; template <class T> inline void Rd(T &res){ char c;res=0;int k=1; while(c=getchar(),c<48&&c!='-'); if(c=='-'){k=-1;c='0';} do{ res=(res<<3)+(res<<1
)+(c^48); }while(c=getchar(),c>=48); res*=k; } int n,m,p; int val[M][M];//i時刻第j段公路出現的金幣數 int cost[M];//第i個工廠的製造費用 int dp[M][M];//i時刻在j工廠的最多金幣 int step[M][M];//i時刻在j工廠的機器人走的步數 int pre[M];//i工廠前一個工廠的編號 int pastmx,mx; int main(){ Rd(n);Rd(m);Rd(p); for(int j=1;j<=n;j++) for(int i=1;i<=m;i++) Rd(val[i][j]); for(int i=1;i<=n;i++){ Rd(cost[i]); pre[i]=i-1; } pre[1]=n; mx=-1<<30; for(int i=1;i<=n;i++){ step[1][i]=1; dp[1][i]=val[1][pre[i]]-cost[pre[i]]; mx=max(mx,dp[1][i]); } pastmx=mx; for(int i=2;i<=m;i++){ mx=-1<<30; for(int j=1;j<=n;j++){ if(step[i-1][pre[j]]<p&&pastmx-cost[pre[j]]<dp[i-1][pre[j]]){ step[i][j]=step[i-1][pre[j]]+1; dp[i][j]=dp[i-1][pre[j]]+val[i][pre[j]]; }else{ step[i][j]=1; dp[i][j]=pastmx-cost[pre[j]]+val[i][pre[j]]; } mx=max(mx,dp[i][j]); } pastmx=mx; } printf("%d\n",mx); return 0; }

剛看到這題的時候,直接敲了O(nmp)的DP,沒往貪心的方面想,所以沒做出來,現在想想還是很清晰的。