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];然后套模板;

 #include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<vector>
#include<cstdlib>
#include<cstring>
#include<set>
#include<map>
#include<queue>
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define pbk push_back
#define mk make_pair
using namespace std;
typedef long long LL;
const int N = +;
const double eps = 1e-;
inline int dcmp(double x) {
return x < -eps ? - : x > eps;
}
LL x[N],sw[N],swx[N],w[N];
int n,k;
LL dp[N][N];
struct Point{
LL x,y;
Point (LL x = , LL y = ):x(x),y(y){}
Point operator - (const Point &p)const{
return Point(x - p.x, y - p.y);
}
LL operator * (const Point &p)const{
return x * p.y - y * p.x;
}
};
struct dequeue{
int head,tail;
Point q[N];
void init(){
head = ; tail = ;
}
void push(const Point &u){
while (head < tail && (q[tail] - q[tail - ]) * (u - q[tail - ]) <= ) tail--;
q[++tail] = u;
}
Point pop(const LL &k) {
while (head < tail && k*q[head].x + q[head].y >= k*q[head+].x + q[head+].y) head++;
return q[head];
}
}H;
void solve(){
sw[] = swx[] = ;
for (int i = ; i <= n; i++) {
sw[i] = sw[i-] + w[i];
swx[i] = swx[i-] + w[i]*x[i];
}
memset(dp,,sizeof(dp));
for (int i = ; i <= n; i++) {
dp[][i] = x[i]*sw[i] - swx[i];
}
for (int j = ; j <= k; j++){
H.init(); H.push(Point(sw[j-],swx[j-] + dp[j-][j-]));
for (int i = j; i <= n; i++) {
Point p = H.pop(-x[i]);
dp[j][i] = x[i]*sw[i] - swx[i] - x[i] * p.x + p.y;
H.push(Point(sw[i],swx[i] + dp[j-][i]));
}
}
printf("%lld\n",dp[k][n]); }
int main(){
while (~scanf("%d%d",&n,&k)) {
for (int i = ; i <= n; i++) {
scanf("%lld%lld",&x[i],&w[i]);
}
solve();
}
return ;
}

cf319C

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

 /*
题意:有n课树要砍,每次只能砍1个单位的长度,每次砍完后都要花费已经砍完的id最大的树的bi时间充电,a1 = 1;
并且 bn = 0; 问最少花多少时间把树砍完; 设DP[i]表示使当前已经砍完的树的最大ID是i的最小花费
方程:DP[i] = DP[j] + b[j] + (a[i]-1)*b[j]; //最后一棵树只需要a[i]-1次,第一b[j]表示补上上一棵树的最后一次; 然后就是标准的斜率DP;
DP[i] = DP[j] + a[i]*b[j];
设 b[j] = x[j]; DP[j] = y[j];
G = a[i]*x[j]+y[j];
因为x[j]是单调递减的,y[j]是单调递增的,a[i]是单调递增的;
所以满足条件;
套一下模板就好; trick: 在做Cross()时会爆LL,然后又因为我那样写u.x,v.x都是负数,移项要变符号,
然后太懒,没有自己造例子,样例一直可以跑出,花了一个晚上找BUG,教训啊!! */
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long LL;
const int N=+;
struct Point{
LL x,y;
Point (LL a=,LL b=):x(a),y(b){}
Point operator - (const Point &p)const{
return Point(x-p.x,y-p.y);
}
};
const double eps = 1e-;
int dcmp(double x){
return x < -eps ? - : x > eps;
}
LL Cross(const Point &u,const Point &v){
if (dcmp(u.x*1.0/v.x - u.y*1.0/v.y) <= ) return ;
return -;
}
Point q[N];
int head,tail;
void init(){
head = ; tail = ;
}
void push(const Point &u){
while (head < tail && Cross(q[tail]-q[tail-], u-q[tail-]) >= ) tail--;
q[++tail] = u;
}
void pop(const LL &k){
while (head < tail && k*q[head].x + q[head].y >= k*q[head+].x + q[head+].y ) head++;
}
int n;
LL a[N],b[N];
LL dp[N];
void solve(){
init();
dp[] = ; push(Point(b[],));
for (int i = ; i <= n; i++) {
pop(a[i]);
dp[i] = a[i] * q[head].x + q[head].y;
push(Point(b[i],dp[i]));
}
printf("%I64d\n",dp[n]);
}
int main(){
freopen("in.txt","r",stdin);
while (~scanf("%d",&n)){
for (int i = ; i <= n; i++) {
scanf("%I64d",a+i);
}
for (int i = ; i <=n; i++) {
scanf("%I64d",b+i);
}
solve(); }
return ;
}

cf311 B

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

 /*
题意:有直线上n个地点,m只cat,p个喂养人,cat会在ti到达hi,喂养人从地点1
任意时间出发,如果碰到已经到达的CAT就照顾,问怎么安排喂养人
使所有CAT被照顾到且,cat等待的时间最少; sum_di[i]表示 地点i到地点1的距离,那么 val[j] = ti[j] - sum_di[hi[j]就表示对cat j
喂养人最早的出发时间;
sort(val); 那么题意就是讲val[]至多分成p段,每段的花费为:
设该段为[i,j],w = val[j]*(j-i+1)- (sum[j] - sum[j-1]); 设DP[j][i]表示 前i个值分成j段的最小花费
方程:DP[j][i] = DP[j-1][k] + val[i]*(i-k) - (sum[i]-sum[k]);
很明显可以化成斜率的DP的模式;
x[k] = k;
y[k] = sum[k] + dp[k];
G = -a[i]*x[k] + y[k]; trick :人出发的时间可以是负的,
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=+;
typedef long long LL;
struct Point{
LL x,y;
Point (LL a=,LL b=):x(a),y(b){}
Point operator - (const Point &p)const{
return Point (x-p.x, y-p.y);
}
};
LL Cross(const Point &u,const Point &v){
return u.x*v.y - u.y*v.x;
} Point q[N];
int head,tail;
void init(){
head = ; tail = ;
}
void push(const Point &u){
while (head < tail && Cross(q[tail]-q[tail-],u-q[tail-]) <= ) tail--;
q[++tail] = u;
}
void pop(const LL &k){
while (head < tail && k*q[head].x + q[head].y >= k*q[head+].x+q[head+].y) head++;
}
int n,m,p;
LL di[N],sum_di[N];
LL sum[N],hi[N],ti[N],val[N];
void init_val(){
for (int i = ; i<=m ;i++){
val[i] = ti[i] - sum_di[hi[i]];
}
sort(val+,val+m+);
sum[] = ;
for (int i = ; i<=m; i++) {
sum[i] = sum[i-] + val[i];
}
/* for (int i =1; i<=m; i++) {
cout << val[i] << " ";
}cout<<endl;
*/
}
LL dp[][N];
void solve(){
init_val();
for (int i = ; i <= m; i++) {
dp[][i] = val[i]*i - sum[i];
}
LL ret = dp[][m];
for (int j = ; j<=p; j++) {
init();
for (int i = ; i <= m; i++) {
pop(-val[i]);
dp[j][i] = -val[i]*q[head].x + q[head].y - sum[i] + val[i]*i;
push(Point(i,dp[j-][i] + sum[i])); }
if (dp[j][m] < ret) ret = dp[j][m];
}
printf("%I64d\n",ret);
}
int main(){
freopen("in.txt","r",stdin);
while (~scanf("%d%d%d",&n,&m,&p)){
for (int i = ; i <= n ;i++) scanf("%I64d",di+i);
sum_di[] = sum_di[] = ;
for (int i = ; i <= n; i++) sum_di[i] = sum_di[i-] + di[i];
for (int i= ; i <= m; i++) scanf("%I64d%I64d",hi+i,ti+i);
solve();
}
return ;
}

hdu 3669

 /*
题意:n个A矩形,至多在墙上挖M个B矩形,每个B矩形的花费是矩形的面积,且每个B矩形不能重叠,
使所有A矩形都能通过,(A矩形不能旋转),问最小的花费; 按照wi从大到小,如果wi相等,则按hi从大到小排,这样对于wi相同的矩形,只要hi最高的能通过,其他
的肯定能通过,这样可以预处理出必要的矩形; 这样问题就变成,给你wi递减,hi递增的矩形序列,至多分成M段,没段的花费为
w[i,j] = mat[i].wi*mat[j].hi; 设DP[j][i]表示将前i个矩形划分成j段的最小花费;
DP[j][i] = DP[j-1][k] + w[k,i]; 很标准的斜率DP; 注意: 常数很大,写在结构体里就T了,还有就是能不用LL的就不要用LL,
用LL的常数也很大,
还有就是一个可以”优化“的,就是if dp[j][n] > ret then break; 标记为“>.<”的那行;
但是不知道是不是对的
*/
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
#include<set>
using namespace std;
const int N=+;
typedef long long LL;
struct Point{
int x;
LL y;
Point (int a=,LL b=):x(a),y(b){}
Point operator - (const Point &p) const{
return Point(x-p.x,y-p.y);
}
};
typedef Point Vector;
inline LL Cross(const Vector &u,const Vector &v){
return (LL)u.x*v.y - (LL)u.y*v.x;
}
struct rec{
int w,h;
bool operator < (const rec &p)const{
return w>p.w || (w==p.w && h>p.h);
}
}mat[N];
int n,M;
Point q[N];
int head,tail;
/*
struct dequeue{
Point q[N];
int head,tail;
void init(){
head = 1; tail = 0;
}
void push(const Point &u){
while (head < tail && Cross(q[tail]-q[tail-1],u-q[tail-1]) >= 0 ) tail--;
q[++tail] = u;
}
Point pop(const LL &k){
while (head < tail && k*q[head].x + q[head].y >= k*q[head+1].x + q[head+1].y ) head++;
return q[head];
}
}H;
*/
LL dp[][N];
void solve(){ sort(mat+,mat+n+);
int tn=;
for (int i=;i<=n;i++){
if (mat[tn].w!=mat[i].w && mat[tn].h < mat[i].h) mat[++tn] = mat[i];
}
n = tn; LL ret = -;
int pos=;
dp[pos][] = ;
for (int i=;i<=n;i++){
dp[pos][i]=(LL)mat[].w*mat[i].h;
}
ret = dp[pos][n];
for (int j=;j<=M;j++){
head = ; tail = ;
for (int i=;i<=n;i++){
Point u=Point(mat[i].w,dp[pos][i-]);
while (head < tail && Cross(q[tail]-q[tail-],u-q[tail-]) >= ) tail--;
q[++tail] = u;
while (head < tail && (LL)q[head].x*mat[i].h + q[head].y >= (LL)q[head+].x*mat[i].h + q[head+].y )
head++; dp[pos^][i] = (LL)q[head].x*mat[i].h + q[head].y;
} pos ^= ; dp[pos][] = ;
if (dp[pos][n] < ret) ret = dp[pos][n];
//else break; >.<
}
printf("%I64d\n",ret);
}
int main(){
//freopen("in.txt","r",stdin);
while (~scanf("%d%d",&n,&M)){
for (int i=;i<=n;i++){
scanf("%d%d",&mat[i].w,&mat[i].h);
}
solve();
}
return ;
}

hdu 3725

 /*
题意:happy Farm,偷菜,且只能按照价值从大到小开始偷(一般斜率DP都是这样,有一定的顺序,然后就是分段了)
每个蔬菜有两个值ai,di,可以刷新M次,每次刷新后DOG的anger值变成0; 抽象一下:给你一个序列,至多分成M段,每段的花费是w[i,j];
在花费小于ti的情况下,使每段sum_di的最大值最小; w[i+1,j] = a[i+1]*1 + a[i+2]*2 + ... + a[j]*(j-i+1); 最大值最小,很容易让人想到二分最大值m,然后判断是否满足; 现在问题变成:每段sum_di的值不超过m,问是否存在方案使得花费小于ti; Ti[i] = a[1]*1 + a[2]*2 + ... + a[i]*i;
sum[i] = a[1] + a[2] + ... + a[i];
w[i+1,j] = Ti[j] - Ti[i] - sum[i]*(j-i); dp[j][i]表前i个蔬菜,分成j段,每段sum_di满足要求下的最小花费;
dp[j[i] = dp[j-1][k] + w[k,i]; 显然也是一个斜率DP模型; 因为每段加了一个sum_di不能超过m的要求,所以在POP时还要POP掉不能满足条件的点
这就有可能H.q[]里一个点也没有,anger[i]本身大于m,所以我们在操作时要判断一下,
同样在PUSH时也是,不需要把那些不满足的点push进去; 初始化,和队列的边界问题我想是写斜率DP的唯一要注意一下的地方; */
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
#include<string>
using namespace std;
const int N=+;
typedef long long LL;
struct Point{
LL x,y;
Point (LL a=,LL b=):x(a),y(b){}
Point operator - (const Point &p)const{
return Point(x - p.x, y - p.y);
}
};
LL sum[N],Ti[N],ti;
int n,M,r,anger[N];
typedef Point Vector;
LL Cross(const Vector &u,const Vector &v){
return u.x*v.y - u.y*v.x;
}
struct dequeue{
Point q[N];
int head,tail;
void init(){
head = ; tail = ;
}
void push(const Point &u){
while (head < tail && Cross(q[tail] - q[tail-],u - q[tail-]) <= )
tail--;
q[++tail] = u;
}
void pop(int k,int i,int w){
while (head <= tail && anger[i] - anger[q[head].x] > w) head++;
while (head < tail && -k*q[head].x + q[head].y >= -k*q[head+].x + q[head+].y) head++; }
}H; struct vegetable{
int vi,ai;
LL di;
vegetable(int v=,int a=,LL d=):vi(v),ai(a),di(d){}
bool operator < (const vegetable &p)const{
return vi>p.vi;
}
void input(){
scanf("%d%d%I64d",&vi,&ai,&di);
}
void output(){
cout<< vi <<" "<< ai <<" "<< di << endl;
} }vg[N]; void init(){
sort(vg+,vg+n+);
anger[] = sum[] = Ti[] = ;
for (int i=;i<=n;i++) {
sum[i]=sum[i-]+vg[i].di;
Ti[i]=Ti[i-]+i*vg[i].di;
anger[i] = anger[i-] + vg[i].ai;
}
/*
for (int i=0;i<=n;i++) cout<<sum[i]<<" ";cout<<endl;
for (int i=0;i<=n;i++) cout<<Ti[i]<<" ";cout<<endl;
for (int i=0;i<=n;i++) cout<<anger[i]<<" ";cout<<endl;
*/
}
LL dp[][N];
int check(int m){
for (int i = ; i <= M; i++){
for (int j = ; j <= n; j++) dp[i][j] = -;
}
for (int i=;i<=n;i++){
if (anger[i] <= m) dp[][i]=Ti[i];
else break;
}
for (int j=;j<=M;j++){ H.init();
H.push(Point(,(j-)*r)); for (int i=;i<=n;i++){
H.pop(sum[i],i,m);
if (H.head<=H.tail)
dp[j][i] = -sum[i]*H.q[H.head].x + H.q[H.head].y + Ti[i] + r;
//cout<< t.x << " " << t.y << " *** " <<dp[j][i]<< endl;
if (dp[j-][i] != -) H.push(Point(i,sum[i]*i + dp[j-][i] - Ti[i])); }
}
LL ret = dp[][n];
for (int i=; i<=M;i++){
if(dp[i][n] < ret || ret == -) ret = dp[i][n];
}
if (ret > ti || ret == -) return ;
return ;
}
void solve(int l,int r){
init();
int ret=-;
while (r>=l){
int m=(l+r)>>;
if (check(m)){
ret=m; r=m-;
}else l=m+;
}
if (ret == -) printf("I have no idea\n");
else printf("%d\n",ret);
}
int main(){ freopen("in.txt","r",stdin);
int T; scanf("%d",&T);
while (T--){
scanf("%d%d%d%I64d",&n,&M,&r,&ti);
int allai = ;
for (int i=;i<=n;i++){
vg[i].input();
allai += vg[i].ai;
}
solve(,allai);
}
return ;
}

斜率DP题目的更多相关文章

  1. 斜率DP个人理解

    斜率DP 斜率DP的一版模式:给你一个序列,至多或分成m段,每段有花费和限制,问符合情况的最小花费是多少: 一版都用到sum[],所以符合单调,然后就可以用斜率优化了,很模板的东西: 如果看不懂可以先 ...

  2. bzoj4518: [Sdoi2016]征途--斜率DP

    题目大意:把一个数列分成m段,计算每段的和sum,求所有的sum的方差,使其最小. 由方差*m可以化简得ans=m*sigma(ki^2)-sum[n]^2 很容易得出f[i][j]=min{f[i- ...

  3. DP题目列表/弟屁专题

    声明: 1.这份列表不是我原创的,放到这里便于自己浏览和查找题目. ※最近更新:Poj斜率优化题目 1180,2018,3709 列表一:经典题目题号:容易: 1018, 1050, 1083, 10 ...

  4. HDU 3480 - Division - [斜率DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3480 Time Limit: 10000/5000 MS (Java/Others) Memory L ...

  5. POJ 1180 - Batch Scheduling - [斜率DP]

    题目链接:http://poj.org/problem?id=1180 Description There is a sequence of N jobs to be processed on one ...

  6. HDU 2829 - Lawrence - [斜率DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2829 T. E. Lawrence was a controversial figure during ...

  7. HDU 2993 - MAX Average Problem - [斜率DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2993 Consider a simple sequence which only contains p ...

  8. HDU 3045 picnic cows(斜率DP)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3045 题目大意:有n个数,可以把n个数分成若干组,每组不得小于m个数,每组的价值=除了该组最小值以外每 ...

  9. HDU 3669 Cross the Wall(斜率DP+预处理)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3669 题目大意:有n(n<=50000)个矩形,每个矩形都有高和宽,你可以在墙上最多挖k个洞使得 ...

随机推荐

  1. apk反编译(7)用ProGuard混淆代码,初级防止反编译

    eclipse为例 1,project.properties去掉 #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:pro ...

  2. 自定义View(6)paint设置图图层重叠时的显示方式,包含清空canvas

    Paint.setXfermode 这个函数设置两个图层相交时的模式 在已有的图层上绘图将会在其上面添加一层新的图层. 如果新的图层是完全不透明的,那么它将完全遮挡住下面的图层,而setXfermod ...

  3. Huge CSV and XML Files in Python, Error: field larger than field limit (131072)

    Huge CSV and XML Files in Python January 22, 2009. Filed under python twitter facebook pinterest lin ...

  4. hdu 4901 The Romantic Hero (dp)

    题目链接 题意:给一个数组a,从中选择一些元素,构成两个数组s, t,使s数组里的所有元素异或 等于 t数组里的所有元素 位于,求有多少种构成方式.要求s数组里 的所有的元素的下标 小于 t数组里的所 ...

  5. struts2 获取前台表单的值?? 原理??

    struts2中,在ACTION中申明一个变量 private string 变量名:然后设置变量名 的get/set方法: 在运行的时候struts2会自动获取. 比如:jsp 页面中有个文本框&l ...

  6. Android中shape中的属性大全

    ---恢复内容开始--- <shape> <!-- 实心 --> <solid android:color="#ff9d77"/> <!- ...

  7. UVA 11374 Airport Express(最短路)

    最短路. 把题目抽象一下:已知一张图,边上的权值表示长度.现在又有一些边,只能从其中选一条加入原图,使起点->终点的距离最小. 当加上一条边a->b,如果这条边更新了最短路,那么起点st- ...

  8. UVALive 4452 The Ministers' Major Mess(2-sat)

    2-sat.又学到了一种使用的方法:当确定选择某中状态A时,从它的对立状态A^1引一条边add(A^1,A),从而使凡是dfs经过对立状态,必然return false:即保证若存在一种可能性,必然是 ...

  9. codevs 3732 解方程

    神题不可言会. f(x+p)=f(x)(mod p) #include<iostream> #include<cstdio> #include<cstring> # ...

  10. ti processor sdk linux am335x evm /bin/setup-tftp.sh hacking

    #!/bin/sh # # ti processor sdk linux am335x evm /bin/setup-tftp.sh hacking # 说明: # 本文主要对TI的sdk中的setu ...