zoukankan      html  css  js  c++  java
  • poj1821 Fence【队列优化线性DP】

    Fence
    Time Limit: 1000MS   Memory Limit: 30000K
    Total Submissions: 6122   Accepted: 1972

    Description

    A team of k (1 <= K <= 100) workers should paint a fence which contains N (1 <= N <= 16 000) planks numbered from 1 to N from left to right. Each worker i (1 <= i <= K) should sit in front of the plank Si and he may paint only a compact interval (this means that the planks from the interval should be consecutive). This interval should contain the Si plank. Also a worker should not paint more than Li planks and for each painted plank he should receive Pi $ (1 <= Pi <= 10 000). A plank should be painted by no more than one worker. All the numbers Si should be distinct. 

    Being the team's leader you want to determine for each worker the interval that he should paint, knowing that the total income should be maximal. The total income represents the sum of the workers personal income. 

    Write a program that determines the total maximal income obtained by the K workers. 

    Input

    The input contains: 
    Input 

    N K 
    L1 P1 S1 
    L2 P2 S2 
    ... 
    LK PK SK 

    Semnification 

    N -the number of the planks; K ? the number of the workers 
    Li -the maximal number of planks that can be painted by worker i 
    Pi -the sum received by worker i for a painted plank 
    Si -the plank in front of which sits the worker i 

    Output

    The output contains a single integer, the total maximal income.

    Sample Input

    8 4
    3 2 2
    3 2 3
    3 3 5
    1 1 7 
    

    Sample Output

    17

    Hint

    Explanation of the sample: 

    the worker 1 paints the interval [1, 2]; 

    the worker 2 paints the interval [3, 4]; 

    the worker 3 paints the interval [5, 7]; 

    the worker 4 does not paint any plank 

    Source

     
    题意:
    k个工人粉刷n道墙,每个工人最多刷L[i]面,并且必须刷第S[i]面,每刷一面可以拿到P[i]的报酬。一面墙至多只能被刷一次。
    思路:
    首先我们按照每个工人的S进行排序,就可以保证所有工人可以做到在上一个工人之后粉刷。
    用dp[i][j]来表示前i个工人粉刷前j面墙最多所得报酬。
    对于一个工人i,他可以刷也可以不刷。对于一面墙j,他可以被刷也可以不被刷。
    所以dp[i][j] = max(dp[i -1][j], dp[i][j -1])
    dp[i][j] 的第三种情况是第i个工人从第k+1块刷到第j块。 k+1 <= Si <= j, j - k<= Li
    可以得到状态转移方程:

    当我们循环j,k时,可以把i看成是定值,于是对于这条方程我们可以进行一定的变形,将j和k分离。

     
    当j增大时,k的取值上界不变,下界增大。
    如果现在有两个决策k1,k2. 且k1 < k2 <=Si-1,如果还满足dp[i-1,k1] - Pi * k1<= dp[i -1, k2] - Pi * k2
    那么k2不但比k1更优,存活时间也更长,此时k1就是一个无用决策。
    所以可以维护一个决策点k单调递增,dp[i-1,k] - Pi * k单调递减的队列。
    当 j 变大时,检查队头,将小于j-Li的决策出队
    查询最优决策时,队头即为所求。
    当有一个新的决策需要入队时,检查队尾单调性,把无用决策出队,最后新决策入队。
     
     1 #include <iostream>
     2 #include <set>
     3 #include <cmath>
     4 #include <stdio.h>
     5 #include <cstring>
     6 #include <algorithm>
     7 #include <map>
     8 using namespace std;
     9 typedef long long LL;
    10 #define inf 0x7f7f7f7f
    11 
    12 int k, n;
    13 struct node{
    14     int l, s, p;
    15 }worker[110];
    16 int que[16010], dp[110][16010];
    17 bool cmp(node a, node b)
    18 {
    19     return a.s < b.s;
    20 }
    21 
    22 int cal(int i, int k)
    23 {
    24     return dp[i - 1][k] - worker[i].p * k;
    25 }
    26 
    27 int main()
    28 {
    29     //freopen("in.txt", "r", stdin);
    30     //freopen("out.txt", "w", stdout);
    31     while(scanf("%d%d", &n, &k)!= EOF){
    32         for(int i = 1; i <= k; i++){
    33             scanf("%d%d%d", &worker[i].l, &worker[i].p, &worker[i].s);
    34             //printf("%d%d%d
    ", worker[i].l, worker[i].s, worker[i].p);
    35         }
    36         sort(worker + 1, worker + 1 + k, cmp);
    37 
    38         /*for(int i = 1; i <= k; i++){
    39             printf("%d%d%d
    ", worker[i].l, worker[i].s, worker[i].p);
    40         }*/
    41         for(int i = 1; i <= k; i++){
    42             int l = 1, r = 0;
    43             for(int x = max(0, worker[i].s - worker[i].l); x <= worker[i].s - 1; x++){
    44                 while(l <= r && cal(i, que[r]) <= cal(i, x)){
    45                     r--;
    46                 }
    47                 que[++r] = x;
    48             }
    49             for(int j = 1; j <= n; j++){
    50                 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
    51                 if(j >= worker[i].s){
    52                     while(l <= r && que[l] < j - worker[i].l)l++;
    53                     if(l <= r){
    54                         dp[i][j] = max(dp[i][j], cal(i, que[l]) + worker[i].p * j);
    55                     }
    56                     //cout<<dp[i][j]<<endl;
    57                 }
    58             }
    59         }
    60 
    61         //int ans = 0;
    62         //for(int i = 1; i <= n; i++){
    63         //    ans = max(ans, dp[k][i]);
    64         //}
    65         printf("%d
    ", dp[k][n]);
    66     }
    67 }
  • 相关阅读:
    论工作动力的来源是什么?答案是来自于实现自己的梦想
    向梦想者致敬
    内置函数,递归函数,模块与包,开发目录规范
    迭代器,生成器
    闭包函数,装饰器,语法糖
    函数对象,名称空间及查找,作用域
    函数调用与参数
    字符编码
    文件处理
    python 11.5数据类型及常用方法
  • 原文地址:https://www.cnblogs.com/wyboooo/p/9811019.html
Copyright © 2011-2022 走看看