斜率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\n",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\n",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\n",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\n",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\n");
142     else printf("%d\n",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 }

 

posted @ 2013-07-12 01:30  Rabbit_hair  阅读(786)  评论(0编辑  收藏  举报