CSU 1963 Feed the rabbit(斜率優化dp)


http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1963

題意:
有m個坑,每只兔子會在ti時刻回到坑中,現在有n個人,每個人都可以從任意時間(<0也可以)從第一個坑出發,速度為1,如果路過的坑中有兔子,就給它喂食物,每只兔子喂一次即可,計算所有兔子等待時間的最短之和。

 

思路:
對於第i只兔子,如果有個人在y時間正好經過喂了它,那么那個人出發的時間為y-x。現在現計算出每只兔子的y-x值,記為t【】,並排好序。sum【】表示前i只兔子的y-x值。

接下來就是動態轉移了,d【i】【j】表示第i個人正好經過第j只兔子時的最小等待時間。

那么狀態轉移方程就是

d[i][j]=min(d[i][j],d[i-1][k]+(j-k)*t[j]-(sum[j]-sum[k]))

解釋一下這個方程的意思:d【i-1】【k】表示上一個人正好經過第k只兔子時的最小等待時間,那么j~k之間的兔子肯定是要輪到第i個人來喂了,因為第i個人的出發時間更晚一些,所以j~k這些兔子肯定是要等待的,等待的時間就是,也就是上式的(j-k)*t[j]-(sum[j]-sum[k])。

 

理解了這個之后,接下來就是斜率優化dp的應用了,這樣的狀態轉移方程也容易讓人想到斜率優化dp。

設z<k<j,如果k的決策比z的決策更好,那么在上述的狀態轉移方程中滿足

d[i-1][k]+(j-k)*t[j]-(sum[j]-sum[k]) < d[i-1][z]+(j-z)*t[j]-(sum[j]-sum[z])

整理得到,那這就是明顯的斜率優化dp了。

 

所謂的斜率優化dp,就是每新插入一個點的時候,需要維護一條下凸的形狀,對於會形成上凸的點,我們可以直接刪去,因為它肯定不會是最優解。

那么如何維護呢?使用單調隊列。

每次依次先檢查隊首的兩個元素,如果滿足上式,那么說明隊首的第二個值更優,可以刪除第一個值,直到第一個值比第二個元素更優。

然后是隊尾的檢查,如果新插入的點使得隊尾的點成為了上凸點,那么就需要刪去這個上凸點,直到沒有上凸點產生。

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<sstream>
 6 #include<vector>
 7 #include<stack>
 8 #include<queue>
 9 #include<cmath>
10 #include<map>
11 #include<set>
12 using namespace std;
13 typedef long long ll;
14 typedef pair<int,int> pll;
15 const int INF = 0x3f3f3f3f;
16 const int maxn = 1e5+ 5;
17 
18 int n, m, p;
19 ll t[maxn];
20 ll Q[maxn];
21 ll sum[maxn];
22 ll dis[maxn];
23 ll d[105][maxn];
24 
25 ll dy(int i, int j, int k)
26 {
27     return (d[i-1][j]+sum[j])-(d[i-1][k]+sum[k]);
28 }
29 
30 ll dx(int j, int k)
31 {
32     return j-k;
33 }
34 
35 int main()
36 {
37     //freopen("in.txt","r",stdin);
38     while(~scanf("%d%d%d",&n, &m, &p))
39     {
40         dis[1]=0;
41         for(int i=2;i<=n;i++)
42         {
43             scanf("%I64d",&dis[i]);
44             dis[i]+=dis[i-1];
45         }
46 
47         for(int i=1;i<=m;i++)
48         {
49             int x,y;
50             scanf("%d%d",&x,&y);
51             t[i]=y-dis[x];
52         }
53 
54         sort(t+1,t+m+1);
55         sum[0]=0;
56         for(int i=1;i<=m;i++)
57             sum[i]=sum[i-1]+t[i];
58 
59         for(int i=1;i<m;i++)
60             d[1][i]=t[i]*i-sum[i];
61 
62 
63         for(int i=2;i<=p;i++)
64         {
65             int frt=0, rear=-1;
66             for(int j=1;j<=m;j++)
67             {
68                 while(frt<rear && dy(i,Q[frt+1],Q[frt])<t[j]*dx(Q[frt+1],Q[frt]))  frt++;
69                 while(frt<rear && (dy(i,Q[rear],Q[rear-1])*dx(j,Q[rear]))>=(dy(i,j,Q[rear])*dx(Q[rear],Q[rear-1])))
70                     rear--;
71                 Q[++rear]=j;
72 
73                 int tmp=Q[frt];
74                 d[i][j]=d[i-1][tmp]-(tmp-j)*t[j]-(sum[j]-sum[tmp]);
75             }
76         }
77         cout<<d[p][m]<<endl;
78     }
79     return 0;
80 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM