zoukankan      html  css  js  c++  java
  • POJ1821 Fence

    传送门

    这道题是一道很好的单调队列优化DP的例子。

    题目大意是有n个工人,每个人可以粉刷一段长度不超过l[i]的墙,如果一个人粉刷了那么他必须要粉刷第s[i]块墙,一个人粉刷一块墙能得到p[i]的钱,求所有工人得到的钱的最大值。

    我们首先把所有工人按s[i]排序,这样方便我们线性DP。

    考虑DP,用dp[i][j]表示前i个工人刷j块的得到的钱的最大值,那么我们分类讨论,首先是一个人不刷和一块墙不刷的情况,那么就有dp[i][j] = max(dp[i-1][j],dp[i][j-1]);

    之后,我们考虑这个工人刷墙的情况。因为这个人必须要刷s[i],那这个人的转移必然是从s[i] - l[i] ~ s[i]这一段转移过来的。否则的话就成了不合法情况。那么就有dp[i][j] = max{dp[i-1][k] + (j - k) * p[i]} (s[i] - l[i] <= k <= s[i])

    这个式子我们朴素的做法是先枚举i,对于每一个工人i,枚举它粉刷的块数j,之后再枚举转移的范围。不过这样会超时,我们考虑优化。我们发现如果把j,k分离出来,那么对于每一个j(状态),它对应的j * p[i]是一个定值,只有内部的式子随着k(决策)的变化而改变,而且其实对于每一个j,有很大一部分的可选取的决策区间是重复的。

    说到这里,我们就想到用单调队列维护啦!!那么我们的想法就是,对于每一个工人,我们首先把s[i] - l[i] ~ s[i]这一段区间之内的所有决策压入单调队列,之后随着j的增加,所能选取的k的区间左端点随之变大。也就是说实际上,我们只要维护一段左端点变大,右端点不动的区间最大值就可以了。

    所以一般来说,单调队列优化DP的套路就是,如果一个DP方程能被拆成每一个状态都是从一个决策区间中选取最优的转移,然后决策区间随着状态变化左右端点变化,而且有大量重复,我们就可以先拆分式子,拆成内层只与决策有关,这样直接用单调队列维护决策的极值,就省去了一层循环,优化了时间。

    看一下代码。

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cmath>
    #include<set>
    #include<queue>
    #define rep(i,a,n) for(int i = a;i <= n;i++)
    #define per(i,n,a) for(int i = n;i >= a;i--)
    #define enter putchar('
    ')
    
    using namespace std;
    typedef long long ll;
    const int M = 10005;
    const int INF = 1000000009;
    
    int read()
    {
       int ans = 0,op = 1;
       char ch = getchar();
       while(ch < '0' || ch > '9')
       {
          if(ch == '-') op = -1;
          ch = getchar();
       }
       while(ch >= '0' && ch <= '9')
       {
          ans *= 10;
          ans += ch - '0';
          ch = getchar();
       }
       return ans * op;
    }
    
    struct worker
    {
       int l,p,s;
       bool operator < (const worker &g) const
       {
          return s < g.s;
       }
    }a[105];
    
    int n,k,dp[105][20005],q[20005],head,tail;
    
    int calc(int i,int x)
    {
       return dp[i-1][x] - x * a[i].p;
    }
    
    int main()
    {
       n = read(),k = read();
       rep(i,1,k) a[i].l = read(),a[i].p = read(),a[i].s = read();
       sort(a+1,a+1+k);
       rep(i,1,k)
       {
          head = 1,tail = 0;
          rep(j,max(0,a[i].s-a[i].l),a[i].s-1)
          {
         while(head <= tail && calc(i,q[tail]) < calc(i,j)) tail--;
         q[++tail] = j;
          }
          rep(j,1,n)
          {
         dp[i][j] = max(dp[i-1][j],dp[i][j-1]);
         if(j >= a[i].s)
         {
            while(head <= tail && q[head] < j - a[i].l) head++;
            if(head <= tail) dp[i][j] = max(dp[i][j],calc(i,q[head]) + j * a[i].p);
         }
          }
       }
       printf("%d
    ",dp[k][n]);
       return 0;
    }
  • 相关阅读:
    ios-点击图片放大,背景变半透明
    为代码分段标识
    方法的标签_With携带
    使用json要导入什么包
    Json中不支持任何形式的注释,那我们要怎么解决呢
    JFinal中文件上传后会默认放置到WebContent的upload包下,但是tomcat会自动重启,当我们再次打开upload文件夹查看我们刚刚上传的文件时,发现上传的文件已经没有了。
    JFinal上传文件时用getFile()方法报错
    JFinal文件上传时直接使用getPara()去接受表单的数据接收到的数据一直是null?
    Freemarker全部文档和具体实例
    Eclipse安装Freemarker插件
  • 原文地址:https://www.cnblogs.com/captain1/p/9929440.html
Copyright © 2011-2022 走看看