• 斜率DP题目


    uva 12524

    题意:沿河有n个点,每个点有w的东西,有一艘船从起点出发,沿途可以装运东西和卸载东西,船的容量无限,每次把wi的东西从x运到y的花费为(y-x)*wi;

    问把n个点的东西合并成k个的最小花费;

    分析:设dp[j][i]表示把前i个点的东西合并成j个点的最小花费,那么dp[j][i] = min( dp[j-1][k] + w[k+1]*(x[i] - x[k+1]) + w[k+2]*(x[i] - x[k+2]) + ... + w[i] * (x[i] - x[i]));

    设sw[i] = w[1] + w[2] + ...+w[i];

    swx[i] = w[1]*x[1] + w[2]*x[2] + ... + w[i]*x[i];

    那么 dp[j][i] = min( dp[j-1][k] + x[i] * (sw[i] - sw[k]) - (swx[i] - swx[k]) , 0<k<i );

    显然dp[j][i] = -x[i] * sw[k] + swx[k] + dp[j-1][k] + x[i] * sw[i] - swx[i];

    斜率为 x[i];

    x = sw[k];  y = swx[k] + dp[j-1][k];然后套模板;

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<vector>
     6 #include<cstdlib>
     7 #include<cstring>
     8 #include<set>
     9 #include<map>
    10 #include<queue>
    11 #define lson l,m,rt<<1
    12 #define rson m+1,r,rt<<1|1
    13 #define pbk push_back
    14 #define mk make_pair
    15 using namespace std;
    16 typedef long long LL;
    17 const int N = 1000+10;
    18 const double eps = 1e-8;
    19 inline int dcmp(double x) {
    20     return  x < -eps ? -1 : x > eps;
    21 }
    22 LL x[N],sw[N],swx[N],w[N];
    23 int n,k;
    24 LL dp[N][N];
    25 struct Point{
    26     LL x,y;
    27     Point (LL x = 0, LL y = 0):x(x),y(y){}
    28     Point operator - (const Point &p)const{
    29         return Point(x - p.x, y - p.y);
    30     }
    31     LL operator * (const Point &p)const{
    32         return x * p.y - y * p.x;
    33     }
    34 };
    35 struct dequeue{
    36     int head,tail;
    37     Point q[N];
    38     void init(){
    39         head = 1; tail = 0;
    40     }
    41     void push(const Point &u){
    42         while (head < tail && (q[tail] - q[tail - 1]) * (u - q[tail - 1]) <= 0 ) tail--;
    43         q[++tail] = u;
    44     }
    45     Point pop(const LL &k) {
    46         while (head < tail && k*q[head].x + q[head].y >= k*q[head+1].x + q[head+1].y) head++;
    47         return q[head];
    48     }
    49 }H;
    50 void solve(){
    51     sw[0] = swx[0] = 0;
    52     for (int i = 1; i <= n; i++) {
    53         sw[i] = sw[i-1] + w[i];
    54         swx[i] = swx[i-1] + w[i]*x[i];
    55     }
    56     memset(dp,0,sizeof(dp));
    57     for (int i = 1; i <= n; i++) {
    58         dp[1][i] = x[i]*sw[i] - swx[i];
    59     }
    60     for (int j = 2; j <= k; j++){
    61         H.init();
    62        
    63         H.push(Point(sw[j-1],swx[j-1] + dp[j-1][j-1]));
    64         for (int i = j; i <= n; i++) {
    65             Point p = H.pop(-x[i]);
    66             dp[j][i] = x[i]*sw[i] - swx[i] - x[i] * p.x + p.y;
    67             H.push(Point(sw[i],swx[i] + dp[j-1][i]));
    68         }
    69     }
    70     printf("%lld
    ",dp[k][n]);
    71 
    72 }
    73 int main(){
    74     while (~scanf("%d%d",&n,&k)) {
    75         for (int i = 1; i <= n; i++) {
    76             scanf("%lld%lld",&x[i],&w[i]);
    77         }
    78         solve();
    79     }
    80     return 0 ;
    81 }
    View Code

    cf319C 

    http://codeforces.com/contest/319/problem/C

     1 /*
     2  题意:有n课树要砍,每次只能砍1个单位的长度,每次砍完后都要花费已经砍完的id最大的树的bi时间充电,a1 = 1;    
     3  并且 bn = 0; 问最少花多少时间把树砍完;
     4 
     5  设DP[i]表示使当前已经砍完的树的最大ID是i的最小花费
     6  方程:DP[i] = DP[j] + b[j] + (a[i]-1)*b[j];    //最后一棵树只需要a[i]-1次,第一b[j]表示补上上一棵树的最后一次;
     7 
     8  然后就是标准的斜率DP;
     9  DP[i] = DP[j] + a[i]*b[j];
    10  设 b[j] = x[j]; DP[j] = y[j];
    11  G = a[i]*x[j]+y[j];
    12  因为x[j]是单调递减的,y[j]是单调递增的,a[i]是单调递增的;
    13  所以满足条件;
    14  套一下模板就好;
    15  
    16  trick: 在做Cross()时会爆LL,然后又因为我那样写u.x,v.x都是负数,移项要变符号,
    17  然后太懒,没有自己造例子,样例一直可以跑出,花了一个晚上找BUG,教训啊!!
    18 
    19  */
    20 #include<cstring>
    21 #include<cstdlib>
    22 #include<cstdio>
    23 #include<cmath>
    24 #include<iostream>
    25 #include<algorithm>
    26 #include<vector>
    27 using namespace std;
    28 typedef long long LL;
    29 const int N=100000+10;
    30 struct Point{
    31     LL x,y;
    32     Point (LL a=0,LL b=0):x(a),y(b){}
    33     Point operator - (const Point &p)const{
    34         return Point(x-p.x,y-p.y);
    35     }
    36 };
    37 const double eps = 1e-10;
    38 int dcmp(double x){
    39     return x < -eps ? -1 : x > eps;
    40 }
    41 LL Cross(const Point &u,const Point &v){
    42     if (dcmp(u.x*1.0/v.x - u.y*1.0/v.y) <= 0) return 1;
    43     return -1;
    44 }
    45 Point q[N];
    46 int head,tail;
    47 void init(){
    48     head = 1; tail = 0;
    49 }
    50 void push(const Point &u){
    51     while (head < tail && Cross(q[tail]-q[tail-1], u-q[tail-1]) >= 0 ) tail--;
    52     q[++tail] = u;
    53 }
    54 void pop(const LL &k){
    55     while (head < tail && k*q[head].x + q[head].y >= k*q[head+1].x + q[head+1].y ) head++;
    56 }
    57 int n;
    58 LL a[N],b[N];
    59 LL dp[N];
    60 void solve(){
    61     init();
    62     dp[1] = 0; push(Point(b[1],0));
    63     for (int i = 2; i <= n; i++) {
    64         pop(a[i]);
    65         dp[i] = a[i] * q[head].x + q[head].y;
    66         push(Point(b[i],dp[i]));
    67     }
    68     printf("%I64d
    ",dp[n]);
    69 }
    70 int main(){
    71     freopen("in.txt","r",stdin);
    72     while (~scanf("%d",&n)){
    73         for (int i = 1; i <= n; i++) {
    74             scanf("%I64d",a+i);
    75         }
    76         for (int i = 1; i <=n; i++) {
    77             scanf("%I64d",b+i);
    78         }
    79         solve();
    80     
    81     
    82     }
    83     return 0;
    84 }

     cf311 B

    http://codeforces.com/contest/311/problem/B

     1 /*
     2  题意:有直线上n个地点,m只cat,p个喂养人,cat会在ti到达hi,喂养人从地点1
     3  任意时间出发,如果碰到已经到达的CAT就照顾,问怎么安排喂养人
     4  使所有CAT被照顾到且,cat等待的时间最少;
     5 
     6  sum_di[i]表示 地点i到地点1的距离,那么 val[j] = ti[j] - sum_di[hi[j]就表示对cat j
     7  喂养人最早的出发时间;
     8  sort(val); 那么题意就是讲val[]至多分成p段,每段的花费为:
     9  设该段为[i,j],w = val[j]*(j-i+1)- (sum[j] - sum[j-1]);
    10 
    11  设DP[j][i]表示 前i个值分成j段的最小花费
    12  方程:DP[j][i] = DP[j-1][k] + val[i]*(i-k) - (sum[i]-sum[k]);
    13  很明显可以化成斜率的DP的模式;
    14  x[k] = k;
    15  y[k] = sum[k] + dp[k];
    16  G = -a[i]*x[k] + y[k];
    17 
    18 trick :人出发的时间可以是负的,
    19  */
    20 #include<cstdio>
    21 #include<cstring>
    22 #include<iostream>
    23 #include<cstdlib>
    24 #include<cmath>
    25 #include<algorithm>
    26 using namespace std;
    27 const int N=100000+10;
    28 typedef long long LL;
    29 struct Point{
    30     LL x,y;
    31     Point (LL a=0,LL b=0):x(a),y(b){}
    32     Point operator - (const Point &p)const{
    33         return Point (x-p.x, y-p.y);
    34     }
    35 };
    36 LL Cross(const Point &u,const Point &v){
    37     return u.x*v.y - u.y*v.x;
    38 }
    39 
    40 Point q[N];
    41 int head,tail;
    42 void init(){
    43     head = 1; tail = 0;
    44 }
    45 void push(const Point &u){
    46     while (head < tail && Cross(q[tail]-q[tail-1],u-q[tail-1]) <= 0) tail--;
    47     q[++tail] = u;
    48 }
    49 void pop(const LL &k){
    50     while (head < tail && k*q[head].x + q[head].y >= k*q[head+1].x+q[head+1].y) head++;
    51 }
    52 int n,m,p;
    53 LL di[N],sum_di[N];
    54 LL sum[N],hi[N],ti[N],val[N];
    55 void init_val(){
    56     for (int i = 1; i<=m ;i++){
    57         val[i] = ti[i] - sum_di[hi[i]];
    58     }    
    59     sort(val+1,val+m+1);
    60     sum[0] = 0;
    61     for (int i = 1; i<=m; i++) {
    62         sum[i] = sum[i-1] + val[i];
    63     }
    64 /*    for (int i =1; i<=m; i++) {
    65         cout << val[i] << " ";
    66     }cout<<endl;
    67 */
    68 }
    69 LL dp[110][N];
    70 void solve(){
    71    init_val();
    72    for (int i = 1; i <= m; i++) {
    73            dp[1][i] = val[i]*i - sum[i];
    74    }    
    75    LL ret = dp[1][m];
    76    for (int j = 2; j<=p; j++) {
    77         init();
    78         for (int i = 1; i <= m; i++) {
    79             pop(-val[i]);
    80             dp[j][i] = -val[i]*q[head].x + q[head].y - sum[i] + val[i]*i;
    81             push(Point(i,dp[j-1][i] + sum[i]));    
    82 
    83         }
    84         if (dp[j][m] < ret) ret = dp[j][m];
    85    }
    86    printf("%I64d
    ",ret);
    87 }
    88 int main(){
    89     freopen("in.txt","r",stdin);
    90     while (~scanf("%d%d%d",&n,&m,&p)){
    91         for (int i = 2; i <= n ;i++) scanf("%I64d",di+i);
    92         sum_di[0] = sum_di[1] = 0;
    93         for (int i = 2; i <= n; i++) sum_di[i] = sum_di[i-1] + di[i];
    94         for (int i= 1; i <= m; i++) scanf("%I64d%I64d",hi+i,ti+i);
    95         solve();
    96     }
    97     return 0;
    98 }

     hdu 3669

      1 /*
      2 题意:n个A矩形,至多在墙上挖M个B矩形,每个B矩形的花费是矩形的面积,且每个B矩形不能重叠,
      3        使所有A矩形都能通过,(A矩形不能旋转),问最小的花费;
      4 
      5 按照wi从大到小,如果wi相等,则按hi从大到小排,这样对于wi相同的矩形,只要hi最高的能通过,其他
      6 的肯定能通过,这样可以预处理出必要的矩形;
      7 
      8 这样问题就变成,给你wi递减,hi递增的矩形序列,至多分成M段,没段的花费为
      9 w[i,j] = mat[i].wi*mat[j].hi;
     10 
     11 设DP[j][i]表示将前i个矩形划分成j段的最小花费;
     12 DP[j][i] = DP[j-1][k] + w[k,i];
     13 
     14 很标准的斜率DP;
     15 
     16 注意: 常数很大,写在结构体里就T了,还有就是能不用LL的就不要用LL,
     17 用LL的常数也很大,
     18 还有就是一个可以”优化“的,就是if dp[j][n] > ret then break;  标记为“>.<”的那行;
     19 但是不知道是不是对的
     20 */
     21 #include<cstdio>
     22 #include<cstring>
     23 #include<cstdlib>
     24 #include<iostream>
     25 #include<algorithm>
     26 #include<cmath>
     27 #include<vector>
     28 #include<set>
     29 using namespace std;
     30 const int N=50000+10;
     31 typedef long long LL;
     32 struct Point{
     33     int x;
     34     LL y;
     35     Point (int a=0,LL b=0):x(a),y(b){}
     36     Point operator - (const Point &p) const{
     37         return Point(x-p.x,y-p.y);
     38     }
     39 };
     40 typedef Point Vector;
     41 inline LL Cross(const Vector &u,const Vector &v){
     42     return (LL)u.x*v.y - (LL)u.y*v.x;
     43 }
     44 struct rec{
     45     int w,h;
     46     bool operator < (const rec &p)const{
     47         return w>p.w || (w==p.w && h>p.h);
     48     }
     49 }mat[N];
     50 int n,M;
     51 Point q[N];
     52 int head,tail;
     53 /*    
     54 struct dequeue{
     55     Point q[N];
     56     int head,tail;
     57     void init(){
     58         head = 1; tail = 0;
     59     }
     60     void push(const Point &u){
     61         while (head < tail && Cross(q[tail]-q[tail-1],u-q[tail-1]) >= 0 ) tail--;
     62         q[++tail] = u;
     63     }
     64     Point pop(const LL &k){
     65         while (head < tail && k*q[head].x + q[head].y >= k*q[head+1].x + q[head+1].y ) head++;
     66         return q[head];
     67     }
     68 }H;
     69 */
     70 LL dp[2][N];
     71 void solve(){
     72 
     73     sort(mat+1,mat+n+1);
     74     int tn=1;
     75     for (int i=2;i<=n;i++){
     76         if (mat[tn].w!=mat[i].w && mat[tn].h < mat[i].h) mat[++tn] = mat[i];
     77     }
     78     n = tn;
     79 
     80     LL ret = -1;
     81     int pos=0;
     82     dp[pos][0] = 0;
     83     for (int i=1;i<=n;i++){
     84         dp[pos][i]=(LL)mat[1].w*mat[i].h;
     85     }
     86     ret = dp[pos][n];
     87     for (int j=2;j<=M;j++){
     88         head = 1; tail = 0;
     89         for (int i=1;i<=n;i++){
     90             Point u=Point(mat[i].w,dp[pos][i-1]);
     91             while (head < tail && Cross(q[tail]-q[tail-1],u-q[tail-1]) >= 0 ) tail--;
     92             q[++tail] = u;
     93             while (head < tail && (LL)q[head].x*mat[i].h + q[head].y >= (LL)q[head+1].x*mat[i].h + q[head+1].y )
     94                    head++;
     95             
     96             dp[pos^1][i] = (LL)q[head].x*mat[i].h + q[head].y;
     97         }
     98 
     99         pos ^= 1; dp[pos][0] = 0;
    100         if (dp[pos][n] < ret) ret = dp[pos][n];
    101         //else break;  >.<
    102     }
    103     printf("%I64d
    ",ret);
    104 }
    105 int main(){
    106     //freopen("in.txt","r",stdin);
    107     while (~scanf("%d%d",&n,&M)){
    108          for (int i=1;i<=n;i++){
    109             scanf("%d%d",&mat[i].w,&mat[i].h);
    110          }
    111          solve();
    112     }
    113     return 0;
    114 }

    hdu 3725

      1 /*
      2 题意:happy Farm,偷菜,且只能按照价值从大到小开始偷(一般斜率DP都是这样,有一定的顺序,然后就是分段了)
      3 每个蔬菜有两个值ai,di,可以刷新M次,每次刷新后DOG的anger值变成0;
      4 
      5 抽象一下:给你一个序列,至多分成M段,每段的花费是w[i,j];
      6 在花费小于ti的情况下,使每段sum_di的最大值最小;
      7 
      8 w[i+1,j] = a[i+1]*1 + a[i+2]*2 + ... + a[j]*(j-i+1);
      9 
     10 最大值最小,很容易让人想到二分最大值m,然后判断是否满足;
     11 
     12 现在问题变成:每段sum_di的值不超过m,问是否存在方案使得花费小于ti;
     13 
     14 Ti[i] = a[1]*1 + a[2]*2 + ... + a[i]*i;
     15 sum[i] = a[1] + a[2] + ... + a[i];
     16 w[i+1,j] = Ti[j] - Ti[i] - sum[i]*(j-i);
     17 
     18 dp[j][i]表前i个蔬菜,分成j段,每段sum_di满足要求下的最小花费;
     19 dp[j[i] = dp[j-1][k] + w[k,i];
     20 
     21 显然也是一个斜率DP模型;
     22 
     23 因为每段加了一个sum_di不能超过m的要求,所以在POP时还要POP掉不能满足条件的点
     24 这就有可能H.q[]里一个点也没有,anger[i]本身大于m,所以我们在操作时要判断一下,
     25 同样在PUSH时也是,不需要把那些不满足的点push进去;
     26 
     27 初始化,和队列的边界问题我想是写斜率DP的唯一要注意一下的地方;
     28 
     29 */
     30 #include<cstdio>
     31 #include<cstring>
     32 #include<iostream>
     33 #include<cstdlib>
     34 #include<algorithm>
     35 #include<cmath>
     36 #include<vector>
     37 #include<string>
     38 using namespace std;
     39 const int N=30000+10;
     40 typedef long long LL;
     41 struct Point{
     42     LL x,y;
     43     Point (LL a=0,LL b=0):x(a),y(b){}
     44     Point operator - (const Point &p)const{
     45         return Point(x - p.x, y - p.y);
     46     }
     47 };
     48 LL sum[N],Ti[N],ti;
     49 int n,M,r,anger[N];
     50 typedef Point Vector;
     51 LL Cross(const Vector &u,const Vector &v){
     52     return u.x*v.y - u.y*v.x;
     53 }
     54 struct dequeue{
     55     Point q[N];
     56     int head,tail;
     57     void init(){
     58         head = 1; tail = 0;
     59     }
     60     void push(const Point &u){
     61         while (head < tail && Cross(q[tail] - q[tail-1],u - q[tail-1]) <= 0) 
     62             tail--;
     63         q[++tail] = u;
     64     }
     65     void pop(int k,int i,int w){
     66         while (head <= tail && anger[i] - anger[q[head].x] > w) head++;
     67         while (head < tail && -k*q[head].x + q[head].y >= -k*q[head+1].x + q[head+1].y) head++;
     68         
     69     }
     70 }H;
     71 
     72 struct vegetable{
     73     int vi,ai;
     74     LL di;
     75     vegetable(int v=0,int a=0,LL d=0):vi(v),ai(a),di(d){}
     76     bool operator < (const vegetable &p)const{
     77         return vi>p.vi;
     78     }
     79     void input(){
     80         scanf("%d%d%I64d",&vi,&ai,&di);
     81     }
     82     void output(){
     83         cout<< vi <<" "<< ai <<" "<< di << endl;
     84     }
     85     
     86 }vg[N];
     87 
     88 void init(){
     89     sort(vg+1,vg+n+1);
     90     anger[0] = sum[0] = Ti[0] = 0;
     91     for (int i=1;i<=n;i++) {
     92         sum[i]=sum[i-1]+vg[i].di;
     93         Ti[i]=Ti[i-1]+i*vg[i].di;
     94         anger[i] = anger[i-1] + vg[i].ai;
     95     }
     96     /*
     97     for (int i=0;i<=n;i++) cout<<sum[i]<<" ";cout<<endl;
     98     for (int i=0;i<=n;i++) cout<<Ti[i]<<" ";cout<<endl;
     99     for (int i=0;i<=n;i++) cout<<anger[i]<<" ";cout<<endl;    
    100     */
    101 }
    102 LL dp[11][N];
    103 int check(int m){
    104     for (int i = 0; i <= M; i++){
    105         for (int j = 0; j <= n; j++) dp[i][j] = -1;
    106     }
    107     for (int i=0;i<=n;i++){
    108         if (anger[i] <= m) dp[0][i]=Ti[i];
    109         else break;
    110     }
    111     for (int j=1;j<=M;j++){
    112         
    113         H.init();
    114         H.push(Point(0,(j-1)*r));
    115 
    116         for (int i=1;i<=n;i++){
    117             H.pop(sum[i],i,m);
    118             if (H.head<=H.tail)
    119                 dp[j][i] = -sum[i]*H.q[H.head].x + H.q[H.head].y + Ti[i] + r;
    120             //cout<< t.x << " " << t.y << " *** " <<dp[j][i]<< endl;
    121             if (dp[j-1][i] != -1) H.push(Point(i,sum[i]*i + dp[j-1][i] - Ti[i]));
    122         
    123         }
    124     }    
    125     LL ret = dp[0][n];
    126     for (int i=1; i<=M;i++){
    127         if(dp[i][n] < ret || ret == -1) ret = dp[i][n];
    128     }
    129     if (ret > ti || ret == -1) return 0;
    130     return 1;
    131 }
    132 void solve(int l,int r){
    133     init();
    134     int ret=-1;
    135     while (r>=l){
    136         int m=(l+r)>>1;
    137         if (check(m)){
    138             ret=m; r=m-1;
    139         }else l=m+1;
    140     }
    141     if (ret == -1) printf("I have no idea
    ");
    142     else printf("%d
    ",ret);
    143 }
    144 int main(){
    145 
    146     freopen("in.txt","r",stdin);
    147     int T; scanf("%d",&T);
    148     while (T--){
    149         scanf("%d%d%d%I64d",&n,&M,&r,&ti);
    150         int allai = 0;
    151         for (int i=1;i<=n;i++){
    152             vg[i].input();
    153             allai += vg[i].ai;
    154         }
    155         solve(1,allai);
    156     }
    157     return 0;
    158 }
  • 相关阅读:
    Jaeger Client Go 链路追踪|入门详解
    Go 中的 gRPC 入门详解
    【五分钟】001-数据结构概论
    【重榜?】.NET 6 Preview 1 开箱上手!带你尝试新版本更新!
    分布式链路追踪框架的基本实现原理
    【网摘】SQL练习题
    【数据库】E-R图向关系模型转换的规则
    6.0 《数据库系统概论》之关系数据库的规范化理论(数据依赖对表的影响[插入-删除-修改-冗余]、1NF-2NF-3NF-BCNF-4NF、函数依赖与多值依赖)
    【数据库】入门基础概念以及部分题目 记录 +答案+个人分析
    5.0 数据库完整性详解(PRIMARY KEY、REFERENCES、CHECK、CONSTRAINT、DOMAIN、TRIGGER)
  • 原文地址:https://www.cnblogs.com/Rlemon/p/3185360.html
Copyright © 2020-2023  润新知