Hdu 2923 MAX Average Problem (DP_斜率优化)

 

Hdu 2923 MAX Average Problem (DP_斜率优化)

分类: 全部博客 ACM_好题经典题 ACM_动态规划(DP)   336人阅读  评论(0)  收藏  举报

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2993


题目大意:给定一个长度为n的序列,从其中找连续的长度大于m的子序列使得子序列中的平均值最小。


解题思路:经典斜率优化DP,04年周维的论文《浅谈数形结合思想在信息学竞赛中的应用》有很详细的分析,这里只讲下实现。

     本题设子序列长度为x,子序列内和为y,使用单调队列来维护凸包的凸性。每次遍历到新的点时都要先把队尾的凸点给去掉,判断条件是y1 * x2 <= y2 * x1((x1,y1)是队尾的点,(x2,y2)是队尾的前一个点).  然后每次更新答案的时候,要把不可能是最优解的点给去掉,判断条件是y1 * x2 <= y2 * x1((x1,y1)是队头的点,(x2,y2)是队头的后一个点)。

     我的第一次ac代码700+ms,第二次加了输入外挂就成312ms了,然后将sum数组从double改成int成281ms,Rank3.    


测试数据:

Input:
10 6
6 4 2 10 3 8 5 9 4 1
1 1
1
10 3
1 2 3 1 2 3 4 5 6 1

Output:
6.50
1.00
5.00



C艹代码:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX 100001  
  4.   
  5.   
  6. double ans;  
  7. int sum[MAX],n,len;  
  8. int qu[MAX],head,tail;  
  9.   
  10.   
  11. double Solve_DP() {  
  12.   
  13.     int i,j,k,pre,cur;  
  14.     double x1,x2,y1,y2;  
  15.   
  16.   
  17.     ans = 0;  
  18.     qu[tail] = 0;  
  19.     head = tail = 0;  
  20.   
  21.   
  22.     for (i = len; i <= n; ++i) {  
  23.   
  24.         cur = i - len;  
  25.         while (head < tail) {  
  26.             //维护队列内凸包的凸性  
  27.             pre = qu[tail];  
  28.             x1 = cur - pre;  
  29.             y1 = sum[cur] - sum[pre];  
  30.             pre = qu[tail-1];  
  31.             x2 = cur - pre;  
  32.             y2 = sum[cur] - sum[pre];  
  33.   
  34.   
  35.             if (y1 * x2 <= y2 * x1) tail--;  
  36.             else break;  
  37.         }  
  38.   
  39.   
  40.         qu[++tail] = cur;  
  41.         while (head < tail) {  
  42.             //寻找最优的那个点  
  43.             cur = i;  
  44.             pre = qu[head];  
  45.             x1 = cur - pre;  
  46.             y1 = sum[cur] - sum[pre];  
  47.             pre = qu[head+1];  
  48.             x2 = cur - pre;  
  49.             y2 = sum[cur] - sum[pre];  
  50.   
  51.   
  52.             if (y1 * x2 <= y2 * x1) head++;  
  53.             else break;  
  54.         }  
  55.   
  56.   
  57.         pre = qu[head];  
  58.         double temp = (sum[i] - sum[pre]) * 1.0 / (i - pre);  
  59.         if (temp > ans) ans = temp;  
  60.     }  
  61.   
  62.   
  63.     return ans;  
  64. }  
  65. int Input() {  
  66.       
  67.     char ch = ' ';  
  68.     while (ch < '0' || ch > '9')  
  69.         ch = getchar();  
  70.     int x = 0;  
  71.     while (ch >= '0' && ch <= '9')  
  72.         x = x * 10 + ch - '0',ch = getchar();  
  73.     return x;  
  74. }  
  75.   
  76.   
  77. int main()  
  78. {  
  79.     int i,j,k;  
  80.   
  81.   
  82.     while (scanf("%d%d",&n,&len) != EOF) {  
  83.   
  84.         for (i = 1; i <= n; ++i)  
  85.             k = Input(),sum[i] = sum[i-1] + k;  
  86.   
  87.   
  88.         ans = Solve_DP();  
  89.         printf("%.2lf\n",ans);  
  90.     }  
  91. }  

本文ZeroClock原创,但可以转载,因为我们是兄弟。

 

Hdu 3507 Print Article (DP_斜率优化)

分类: 全部博客 ACM_动态规划(DP)   440人阅读  评论(0)  收藏  举报

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3507


题目大意:给定一个长度为n的序列,和一个常数m,我们可以将序列分成随意段,每段的权值为sum(arr[i]) + C(x<=i<=y),求一种划分方法使得整个序列的权值最小.n<=50万。


解题思路:做完Hdu的2829,然后再看这题,一切变得如此简单,用两种方法解。

     状态转移方程为: dp[i] = min(dp[j] + (sum[i]-sum[j])^2 + m) (j < i);

     方法一:dp[i] = dp[j] + (sum[i]-sum[j])^2 + m = dp[j] + sum[i] * sum[i] + sum[j] * sum[j] - 2 * sum[i] * sum[j] + m;

     设y =  dp[j] + sum[j] * sum[j],x = sum[j],那么原式等于:dp[i] = y + 2 * sum[i] * x + m + sum[i] * sum[i],然后套下斜率优化DP模板即可ac。

     方法二:方法二使用的优化技巧类似于四边形不等式,用个s[i] 记录dp[i]由前面的哪个状态转移过来,然后枚举的时候只要枚举s[i-1] 到i-1就可以了。

     第二种方法似乎比第一种要慢一些,常数比较大。


测试数据:

Input
5 5
5 9 5 7 5
1 1000
1
3 1000
1 3 5
3 0
1 3 5
1 0
100000

OutPut:
230
1001
1081
35
10000000000


C艹代码:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX 510000  
  4. #define int64 long long  
  5.   
  6.   
  7. struct point{  
  8.   
  9.     int64 x,y,c;  
  10. }pot[MAX];  
  11. int n,m,arr[MAX];  
  12. int64 sum[MAX],dp[MAX];  
  13. int qu[MAX],head,tail;  
  14.   
  15.   
  16. int CheckIt(int x,int y,int z) {  
  17.   
  18.     point p0 = pot[x],p1 = pot[y],p2 = pot[z];  
  19.     return (p0.x -p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x) <= 0;  
  20. }  
  21. int NotBest(int x,int y,int k) {  
  22.   
  23.     point p0 = pot[x],p1 = pot[y];  
  24.     return p0.y - k * p0.x > p1.y - k * p1.x;  
  25. }  
  26. int64 Solve_DP() {  
  27.   
  28.     head = tail = 0;  
  29.     qu[tail] = 0;  
  30.     pot[0].x = pot[0].y = 0;  
  31.   
  32.   
  33.     for (int i = 1; i <= n; ++i) {  
  34.   
  35.         pot[i].x = sum[i-1];  
  36.         pot[i].y = dp[i-1] + sum[i-1] * sum[i-1];  
  37.         while (head <= tail - 1 &&  
  38.                 CheckIt(qu[tail-1],qu[tail],i)) tail--;  
  39.   
  40.   
  41.         qu[++tail] = i;  
  42.         while (head + 1 <= tail &&  
  43.                 NotBest(qu[head],qu[head+1],2 * sum[i])) head++;  
  44.         int k = qu[head];  
  45.         dp[i] = pot[k].y - 2 * sum[i] * pot[k].x + sum[i] * sum[i] + m;  
  46.     }  
  47.   
  48.   
  49.     return dp[n];  
  50. }  
  51. int64 Solve_DP2() {  
  52.   
  53.     for (int64 mmin,i = 1; i <= n; ++i) {  
  54.   
  55.         mmin = -1;  
  56.         for (int j = qu[i-1]; j < i; ++j)  
  57.             if (mmin == -1 ||  
  58.                     dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) < mmin) {  
  59.   
  60.                 mmin = dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]);  
  61.                 qu[i] = j;  
  62.             }  
  63.   
  64.   
  65.         dp[i] = mmin + m;  
  66.     }  
  67.     return dp[n];  
  68. }  
  69.   
  70.   
  71. int main()  
  72. {  
  73.     int i,j,k;  
  74.   
  75.   
  76.     while (scanf("%d%d",&n,&m) != EOF) {  
  77.   
  78.         for (i = 1; i <= n; ++i)  
  79.             scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1];  
  80.   
  81.   
  82.         int64 ans = Solve_DP2();  
  83.         printf("%lld\n",ans);  
  84.     }  
  85. }  

本文ZeroClock原创,但可以转载,因为我们是兄弟。

Hdu 3507 Print Article (DP_斜率优化)

分类: 全部博客 ACM_动态规划(DP)   440人阅读  评论(0)  收藏  举报

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3507


题目大意:给定一个长度为n的序列,和一个常数m,我们可以将序列分成随意段,每段的权值为sum(arr[i]) + C(x<=i<=y),求一种划分方法使得整个序列的权值最小.n<=50万。


解题思路:做完Hdu的2829,然后再看这题,一切变得如此简单,用两种方法解。

     状态转移方程为: dp[i] = min(dp[j] + (sum[i]-sum[j])^2 + m) (j < i);

     方法一:dp[i] = dp[j] + (sum[i]-sum[j])^2 + m = dp[j] + sum[i] * sum[i] + sum[j] * sum[j] - 2 * sum[i] * sum[j] + m;

     设y =  dp[j] + sum[j] * sum[j],x = sum[j],那么原式等于:dp[i] = y + 2 * sum[i] * x + m + sum[i] * sum[i],然后套下斜率优化DP模板即可ac。

     方法二:方法二使用的优化技巧类似于四边形不等式,用个s[i] 记录dp[i]由前面的哪个状态转移过来,然后枚举的时候只要枚举s[i-1] 到i-1就可以了。

     第二种方法似乎比第一种要慢一些,常数比较大。


测试数据:

Input
5 5
5 9 5 7 5
1 1000
1
3 1000
1 3 5
3 0
1 3 5
1 0
100000

OutPut:
230
1001
1081
35
10000000000


C艹代码:

[cpp]  view plain copy
  1. #include <stdio.h>  
  2. #include <string.h>  
  3. #define MAX 510000  
  4. #define int64 long long  
  5.   
  6.   
  7. struct point{  
  8.   
  9.     int64 x,y,c;  
  10. }pot[MAX];  
  11. int n,m,arr[MAX];  
  12. int64 sum[MAX],dp[MAX];  
  13. int qu[MAX],head,tail;  
  14.   
  15.   
  16. int CheckIt(int x,int y,int z) {  
  17.   
  18.     point p0 = pot[x],p1 = pot[y],p2 = pot[z];  
  19.     return (p0.x -p1.x) * (p0.y - p2.y) - (p0.y - p1.y) * (p0.x - p2.x) <= 0;  
  20. }  
  21. int NotBest(int x,int y,int k) {  
  22.   
  23.     point p0 = pot[x],p1 = pot[y];  
  24.     return p0.y - k * p0.x > p1.y - k * p1.x;  
  25. }  
  26. int64 Solve_DP() {  
  27.   
  28.     head = tail = 0;  
  29.     qu[tail] = 0;  
  30.     pot[0].x = pot[0].y = 0;  
  31.   
  32.   
  33.     for (int i = 1; i <= n; ++i) {  
  34.   
  35.         pot[i].x = sum[i-1];  
  36.         pot[i].y = dp[i-1] + sum[i-1] * sum[i-1];  
  37.         while (head <= tail - 1 &&  
  38.                 CheckIt(qu[tail-1],qu[tail],i)) tail--;  
  39.   
  40.   
  41.         qu[++tail] = i;  
  42.         while (head + 1 <= tail &&  
  43.                 NotBest(qu[head],qu[head+1],2 * sum[i])) head++;  
  44.         int k = qu[head];  
  45.         dp[i] = pot[k].y - 2 * sum[i] * pot[k].x + sum[i] * sum[i] + m;  
  46.     }  
  47.   
  48.   
  49.     return dp[n];  
  50. }  
  51. int64 Solve_DP2() {  
  52.   
  53.     for (int64 mmin,i = 1; i <= n; ++i) {  
  54.   
  55.         mmin = -1;  
  56.         for (int j = qu[i-1]; j < i; ++j)  
  57.             if (mmin == -1 ||  
  58.                     dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]) < mmin) {  
  59.   
  60.                 mmin = dp[j] + (sum[i] - sum[j]) * (sum[i] - sum[j]);  
  61.                 qu[i] = j;  
  62.             }  
  63.   
  64.   
  65.         dp[i] = mmin + m;  
  66.     }  
  67.     return dp[n];  
  68. }  
  69.   
  70.   
  71. int main()  
  72. {  
  73.     int i,j,k;  
  74.   
  75.   
  76.     while (scanf("%d%d",&n,&m) != EOF) {  
  77.   
  78.         for (i = 1; i <= n; ++i)  
  79.             scanf("%d",&arr[i]),sum[i] = arr[i] + sum[i-1];  
  80.   
  81.   
  82.         int64 ans = Solve_DP2();  
  83.         printf("%lld\n",ans);  
  84.     }  
  85. }  

本文ZeroClock原创,但可以转载,因为我们是兄弟。



你可能感兴趣的:(全部博客,ACM_动态规划(DP),全部博客,ACM_动态规划(DP),ACM_动态规划(DP),全部博客,ACM_好题经典题)