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

NOIP2009普及組 道路遊戲

Description

有一條環形路,路上有n個點,第i個點和第i+1個點有邊相連(第n個點與第1個點有邊相連)。每個點都可以花費
不同的代價生產一個機 器人,且機器人可以順時針走不多於p步(每走一步消耗一單位時間),並撿起此時路上的
金幣。最多隻能有一個機器人存在於路上。不同的時間每條路上金幣數不 同。求最後能夠得到的最大金幣數(即
撿起的金幣數減去生產機器人需要的金幣數)。 

Input

第一行3個正整數,n,m,p,意義如題目所述。 
接下來的n行,每行有m個正整數,每兩個整數之間用一個空格隔開,
其中第i行描述了i號馬路上每個單位時間內出現的金幣數量(1≤金幣數量≤1000),
即第i行的第j(1≤j≤m)個數表示第j個單位時間內i號馬路上出現的金幣數量。 

最後一行,有n個整數,每兩個整數之間用一個空格隔開,
其中第i個數表示在i號機器人工廠購買機器人需要花費的金幣數量(1≤金幣數量≤1000)。 

Output

包含 1 個整數,表示在 m 個單位時間內,扣除購買機器人 
花費的金幣之後,小新最多能收集到多少金幣。 

Sample Input

2 3 2 
1 2 3 
2 3 4 
1 2 

Sample Output

5

實在是沒想到noip普及組會出這樣的題,我保證大多數人這道題寫的是dp,有O(N^3)和O(N^2)的,但是官方給的正解這道題卻是單調佇列

下面給大家解釋一下為什麼可以寫單調佇列:

首先,O(N^3)的寫法還是那麼腦殘(當然得不了滿分),列舉時間,位置,當前走的步數就解決了

其次,O(N^2)其實已經可以算是正解了,具體的我真的也不好講,這個都是從別人那裡得來的經驗

網址:http://blog.csdn.net/yuyanggo/article/details/48685873

我相信那個程式碼寫的很玄學,很難看懂,所以我就加入註釋,算是幫助大家啦(其實我也看了2天才差不多看懂,整整8個小時啊)

程式碼:

#include<cstdio>
#include<algorithm>
using namespace std;
struct oo
{
    int x,y;
}q[1001][1001];
int n,m,p;
int a[1001][1001],b[1001][1001],fm[1001],cost[1001];
void readdata()
{
  int i,j,k;
  scanf("%d%d%d",&n,&m,&p);
  for(i=0;i<n;i++)
    for(j=1;j<=m;j++)
      scanf("%d",&b[i][j]);//讀入部分 
  for(i=0;i<n;i++)scanf("%d",&cost[i]); 
  for(i=0;i<n;i++)a[1][i]=b[i][1];//a[1][i]代表第1個時間點在第i段路上能得到的金幣數 
  for(i=2;i<=m;i++)
    for(j=0;j<n;j++)
      a[i][j]=a[i-1][(j-1+n)%n]+b[j][i];//a[i][j]代表第i個時間點在第j段路上能得到的金幣數 , (j-1+n)%n這裡是在處理環 
}
void del(int t,int yy)
{
  int i,j,k=q[t][0].x;
  for(i=k;q[t][i].y>0;i++)
    if(yy-q[t][i].y<p)break; 
  q[t][0].x=i;  
}
void add(int t,int yy,int xx)
{
  int i,j,k=q[t][0].x;
  if(k==0)//假如當前佇列為空 
    {
      q[t][0].x=1;
      q[t][1].x=xx,q[t][1].y=yy;
      return;
    }
  for(i=k;q[t][i].y>0;i++)
    if(q[t][i].x<=xx)break;//維護佇列單調下降 
  q[t][i].x=xx,q[t][i].y=yy;//加入佇列 
}
void work()
{
  int i,j,k,x;
  for(i=1;i<=m;i++)
    for(fm[i]=-1000000,j=0;j<n;j++)
    {
        k=(i-j+n)%n;//本程式碼的一個玄學點
		/*仔細計算你會發現k的值是有規律的,當k相同時,上次的i和j和和這次的i和j肯定是可以轉移的*/ 
        del(k,i);//有點不確定,但是應該是判斷當前機器人能走的點 
        add(k,i,fm[i-1]-a[i-1][(j-1+n)%n]-cost[j]);//入隊 
        x=a[i][j]+q[k][q[k][0].x].x;//當前能得到的最大價值 
        fm[i]=max(fm[i],x);//統計答案 
    }
    printf("%d",fm[m]);
}
int main()
{
  readdata();
  work();
}