斜率优化DP

hdu 2993 MAX Average Problem

http://acm.hdu.edu.cn/showproblem.php?pid=2993

这题都整死我了,代码基本上和别人AC的都快一样了还是不对。郁闷死了快。。最后终于发现了错误点自判断斜率时y1*x2 <= y2*x1 如果用整型就会超数据类型肯定会错,而上边的用整型可以过,不过最好用实型因为毕竟k的大小不确定。。。这题的详细解释http://www.docin.com/p-47950655.html 例2 这里在转到0-n个点求斜率的时候不好理解。

数形结合以形住树。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll long long

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 100007

#define N 100007

using namespace std;



int q[N],sum[N];

double max(double a,double b)

{

    return a > b ? a:b;

}

int GetInt()

{

    char ch = getchar();

    while (ch < '0' || ch > '9') ch = getchar();

    int num = 0;

    while (ch >= '0' && ch <= '9')

    {

        num = num*10 + ch - '0';

        ch = getchar();

    }

    return num;

}

int main()

{

    //freopen("data.in","r",stdin);

    int n,i,k;

    while (~scanf("%d%d",&n,&k))

    {

        sum[0] = 0;

        for (i = 1; i <= n; ++i)

        {

            int x = GetInt();

            sum[i] = sum[i - 1] + x;

        }

        int front = 0,tail = 0;

        q[0] = 0;

        double ans = 0;

        for (i = k; i <= n; ++i)

        {

            int now = i - k;

            while (tail > front)

            {



                int y1 = sum[q[tail]] - sum[q[tail - 1]];

                int x1 = q[tail] - q[tail - 1];

                int y2 = sum[now] - sum[q[tail]];

                int x2 = now - q[tail];

                if (y1*x2 >= y2*x1) tail--;

                else break;

            }

            q[++tail] = now;



            while(front<tail){

                double y1=sum[i] - sum[q[front]];

                double x1=i - q[front];

                double y2=sum[i] - sum[q[front+1]];

                double x2=i - q[front+1];

                if(y1*x2<=y2*x1)front++;

                else break;

            }

            double tmp = (sum[i] - sum[q[front]])*1.0/(i - q[front]);

            if (tmp > ans) ans = tmp;

        }

        printf("%.2lf\n",ans);

    }

    return 0;

}

 

 

hdu  3507 Print Article

http://acm.hdu.edu.cn/showproblem.php?pid=3507

题意:

给定长度为n的整数序列,斜率优化DP

对他进行划分,每一段的花费为上述公式:假设从i到j为一段划分则花费为 (∑Ck)^2 + m , i<=k<=j。求一个划分使花费最小。

思路:

这里参考别人的思路,给出个人感觉讲解比较好的连接:

http://www.cnblogs.com/ka200812/archive/2012/08/03/2621345.html

代码参考:

http://blog.csdn.net/woshi250hua/article/details/7901433

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll long long

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 100007

#define N 500007

using namespace std;

//freopen("din.txt","r",stdin);



struct point

{

    int x,y;

}pot[N];

int sum[N],q[N];

ll dp[N];

int n,m;

int cross(int x1,int y1,int x2,int y2)

{

    return x1*y2 - x2*y1;

}

int det(point a,point b,point c)

{

    return cross(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y);

}

bool CheckIt(int x,int y,int z)//叉积判断点在线段的方向

{

   int mk = det(pot[x],pot[y],pot[z]);

   if (mk <= 0) return true;

   else return false;

}

bool NotBest(int a,int b,int k)

{

    point p0 = pot[a], p1 = pot[b];

    if (pot[a].y - k*pot[a].x >= pot[b].y - k*pot[b].x) return true;

    else return false;

}

int GetInt()//开挂输入

{

    char ch = getchar();

    while (ch < '0' || ch > '9') ch = getchar();

    int num = 0;

    while (ch >= '0' && ch <= '9')

    {

        num = num*10 + ch - '0';

        ch = getchar();

    }

    return num;



}

int main()

{

    //freopen("din.txt","r",stdin);

    int i;

    while (~scanf("%d%d",&n,&m))

    {

        sum[0] = 0;

        for (i = 1; i <= n; ++i)

        {

            //scanf("%d",&sum[i]);

            sum[i] = GetInt();

            sum[i] += sum[i - 1];//累加和

        }



        int front = 0,tail = 0;

        pot[0].x = pot[0].y = 0;//出事化原点

        q[0] = 0;

        for (i = 1; i <= n; ++i)

        {

            pot[i].x = sum[i - 1];

            pot[i].y = dp[i - 1] + sum[i - 1]*sum[i - 1];//记录每个状态的x,y;

            while (front + 1 <= tail && CheckIt(q[tail - 1],q[tail],i)) tail--;//删除不可能为最优的上凸点

            q[++tail] = i;



            while (front + 1 <= tail && NotBest(q[front],q[front + 1],2*sum[i])) front++;//将在下凸折线不是最有的点删除



            int k = q[front];

            dp[i] = pot[k].y - 2*sum[i]*pot[k].x + sum[i]*sum[i] + m;//获取最优值

        }

        printf("%I64d\n",dp[n]);

    }

    return 0;

}

 

hdu 3408  Division

http://acm.hdu.edu.cn/showproblem.php?pid=3480

题意:

给你一个含有n个数的集合,让你划分成M个子集,每个集合里面的(MAX - MIN)^2为这个集合的花费,求总的花费最小。

思路:

首先我们要经整体排序

我们可以得到状态转移方程dp[i][j]表示以j结尾将序列划分成i块的最小花费则有dp[i][j] = min(dp[i - 1][k] + (a[j] - a[k + 1])*(a[j] - a[k + 1]));这样求的话复杂度O(n*m*m)肯定会超时。所以要优化,我们把方程拆开得到dp[i][j] = dp[i - 1][k] + a[j]^2 + a[k + 1]^ - 2*a[j]*a[k + 1];令y = dp[i - 1][k] + a[k + 1]^2 ; x = a[k + 1].

得到 dp[i][j] = y - 2*a[j]*x + a[j]^2;

则套用斜率优化即可。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll long long

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 5007

#define N 10007

using namespace std;

//freopen("din.txt","r",stdin);



struct point

{

    int x;

    int y;

}pot[N];

int dp[M][N];

int n,m,a[N],q[N];



int cmp(int x,int y)

{

    return x < y;

}

int cross(int x1,int y1,int x2,int y2)

{

    return x1*y2 - x2*y1;

}

int det(point a,point b,point c)

{

    return cross(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y);

}

int GetInt()

{

    char ch = getchar();

    while (ch < '0' || ch > '9') ch = getchar();

    int num = 0;

    while (ch >= '0' && ch <= '9')

    {

        num = num*10 + ch - '0';

        ch = getchar();

    }

    return num;



}

bool CheckIt(int x,int y,int z)

{

    int mk = det(pot[x],pot[y],pot[z]);

    if (mk <= 0) return true;

    else return false;

}

bool NotBest(int a,int b,int k)

{

    if (pot[a].y - k*pot[a].x <= pot[b].y - k*pot[b].x) return true;

    else return false;

}

int main()

{

    //freopen("din.txt","r",stdin);

    int i,j,t,k;

    int cas = 1;

    scanf("%d",&t);

    while (t--)

    {

        scanf("%d%d",&n,&m);

        for (i = 1; i <= n; ++i)

        scanf("%d",&a[i]);



        sort(a + 1,a + 1 + n,cmp);



        for (i = 1; i <= n; ++i)//出事划分成一个集合的

        dp[1][i] = (a[i] - a[1])*(a[i] - a[1]);



        pot[0].x = pot[0].y = 0;

        q[0] = 0;

        for (i = 2; i <= m; ++i)//枚举划分的大小

        {

            int front = 0,tail = 0;

            for (j = i; j <= n; ++j)//枚举可能的结束点

            {

                pot[j].x = a[j];

                pot[j].y = dp[i - 1][j - 1] + a[j]*a[j];

                while (front + 1 <= tail && CheckIt(q[tail - 1],q[tail],j)) tail--;

                q[++tail] = j;



                while (front + 1 <= tail && NotBest(q[front + 1],q[front],2*a[j])) front++;



                k = q[front];

                dp[i][j] = pot[k].y - 2*a[j]*pot[k].x + a[j]*a[j];

            }

        }

        printf("Case %d: %d\n",cas++,dp[m][n]);

    }

    return 0;

}

 

hdu 4258 Covered Walkway

http://acm.hdu.edu.cn/showproblem.php?pid=4258

 和上边的两道题目基本上一样,都是划分的题目。

题意:

给你x坐标上的n个点,让你让你两点连线将所有点覆盖,假设我们在a[i]到a[j]连线,则i-j的点都已经覆盖并且这一段的费用为(j - i)^2,求把所有点覆盖后的最小花费。

思路:

由题可得状态转移方程dp[i] = min(dp[j] + (a[i] - a[j + 1])^2 + c) (1<=j < i) 将方程展开后记得

dp[i] = dp[j] + a[j + 1]^2 + a[i]^2 - 2*a[i]*a[j + 1] + c; 令y = dp[j] + a[j + 1]^2 ; x = a[j + 1]; 则有dp[i] = y - 2*a[i]*x + a[i]^2 + c;

套用斜率优化即可。注意数据类型的处理,这里因为超了数据类型错了一次。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll long long

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 5007

#define N 1000007

using namespace std;

//freopen("din.txt","r",stdin);



struct point

{

    ll x,y;

}pot[N];

ll dp[N];

ll a[N],c;

int q[N];

int n;



ll cross(ll x1,ll y1,ll x2,ll y2)

{

    return x1*y2 - x2*y1;

}

ll det(point a,point b,point c)

{

    return cross(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y);

}

bool CheckIt(int x,int y,int z)

{

    ll mk = det(pot[x],pot[y],pot[z]);

    if (mk <= 0) return true;

    else return false;

}

bool NotBest(int a,int b,ll k)

{

    if (pot[a].y - k*pot[a].x <= pot[b].y - k*pot[b].x) return true;

    else return false;

}

ll GetInt()

{

    char ch = getchar();

    while (ch < '0' || ch > '9') ch = getchar();

    ll num = 0;

    while (ch >= '0' && ch <= '9')

    {

        num = num*10 + ch - '0';

        ch = getchar();

    }

    return num;



}

int main()

{

    //freopen("din.txt","r",stdin);

    int i;

    while (~scanf("%d%I64d",&n,&c))

    {

        if (!n && !c) break;

        for (i = 1; i <= n; ++i) a[i] = GetInt();

         //scanf("%I64d",&a[i]);



        int front = 0,tail = 0;

        pot[0].x = pot[0].y = 0;

        q[0] = 0;



        for (i = 1; i <= n; ++i)

        {

            pot[i].x = a[i];

            pot[i].y = dp[i - 1] + a[i]*a[i];



            while (front + 1 <= tail && CheckIt(q[tail - 1],q[tail],i)) tail--;

            q[++tail] = i;



            while (front + 1 <= tail && NotBest(q[front + 1],q[front],2*a[i])) front++;



            int k = q[front];

            dp[i] = pot[k].y - 2*a[i]*pot[k].x + a[i]*a[i] + c;

            //if (i == 1) printf("%I64d %I64d %I64d %I64d %d\n",pot[i].x,pot[i].y,dp[1],a[i],c);

        }

        printf("%I64d\n",dp[n]);

    }

    return 0;

}

 

hdu 2829  Lawrence

http://acm.hdu.edu.cn/showproblem.php?pid=2829

 

基本上和上几个题类似:

题意:

Lawrence要建一条铁路,铁路包括一些收费站(这里描述为一些点,对应着有自己的vale),还有连接两点的线,这条铁路的花费计算方法为:

4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49.

由于资金材料的限制只能毁掉一些线,使的耗费减小.给定n个点,以及要炸掉m个边求如何炸才能得到最小耗费,输出最小耗费。

思路:

首先我们得到状态转移方程dp[i][j]表示以i结尾炸掉j条路线的最小耗费。则有

dp[i][j] = min(dp[k][j - 1] + cost[k + 1][i]) (1<=k<i). cost[i][j]表示i到j这串数字的乘积和,sum[i]表示1到i为止这些数字的和

cost[1][i] = cost[1][k] + cost[k + 1][i] + sum[k]*(sum[i] - sum[k]);

==>cost[k+1][i]=cost[1][i]-cost[1][k]-sum[k]*(sum[i]-sum[k])

==>dp[i][j]=dp[k][j-1]+cost[1][i]-cost[1][k]-sum[k]*(sum[i]-sum[k])

==>dp[k][j-1]-cost[1][k]+sum[k]^2-sum[i]*sum[k]+cost[1][i]

==>由于sum[i]是与i有关的常量,所以很明显,斜率k=sum[i],
设y=dp[k][j-1]-cost[1][k]+sum[k]^2
那么dp[i][j]=y-k*x+cost[1][i]

到这一步就可以套用斜率优化了。

View Code
#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

#include <cmath>

#include <queue>

#include <stack>

#include <set>

#include <map>

#include <string>



#define CL(a,num) memset((a),(num),sizeof(a))

#define iabs(x)  ((x) > 0 ? (x) : -(x))

#define Min(a,b) (a) > (b)? (b):(a)

#define Max(a,b) (a) > (b)? (a):(b)



#define ll long long

#define inf 0x7f7f7f7f

#define MOD 100000007

#define lc l,m,rt<<1

#define rc m + 1,r,rt<<1|1

#define pi acos(-1.0)

#define test puts("<------------------->")

#define maxn 100007

#define M 5007

#define N 1007

using namespace std;

//freopen("din.txt","r",stdin);

int n,m;

int q[N];

struct point

{

    ll x,y;

}pot[N];

ll cost[N],sum[N],a[N],dp[N][N];



ll cross(ll x1,ll y1,ll x2,ll y2)

{

    return x1*y2 - x2*y1;

}

ll det(point a,point b,point c)

{

    return cross(b.x - a.x,b.y - a.y,c.x - a.x,c.y - a.y);

}

bool CheckIt(int x,int y,int z)

{

    ll mk = det(pot[x],pot[y],pot[z]);

    if (mk <= 0) return true;

    else return false;

}

bool NotBest(int a,int b,ll k)

{

    if (pot[a].y - k*pot[a].x <= pot[b].y - k*pot[b].x) return true;

    else return false;

}



ll solve_DP()

{

    int i,j;

    int front,tail;

    pot[0].x = pot[0].y = 0;

    q[0] = 0;



    for (j = 1; j <= m; ++j)

    {

        front = tail = 0;

        for (i = j + 1; i <= n; ++i)

        {

            pot[i].x = sum[i - 1];

            pot[i].y = dp[i - 1][j - 1] - cost[i - 1] + sum[i - 1]*sum[i -1];

            while (front + 1 <= tail && CheckIt(q[tail - 1],q[tail],i)) tail--;

            q[++tail] = i;



            while (front + 1 <= tail && NotBest(q[front + 1],q[front],sum[i])) front++;



            int k = q[front];

            dp[i][j] = pot[k].y - sum[i]*pot[k].x + cost[i];

        }

    }

    return dp[n][m];

}

ll GetInt()

{

    char ch = getchar();

    while (ch < '0' || ch > '9') ch = getchar();

    ll num = 0;

    while (ch >= '0' && ch <= '9')

    {

        num = num*10 + ch - '0';

        ch = getchar();

    }

    return num;



}

int main()

{

    //freopen("din.txt","r",stdin);

    int i;

    while (~scanf("%d%d",&n,&m))

    {

        if (!n && !m) break;

        sum[0] = 0;

        for (i = 1; i <= n; ++i)

        {

            //scanf("%I64d",&a[i]);

            a[i] = GetInt();

            sum[i] = sum[i - 1] + a[i];

        }

        cost[0] = 0;

        for (i = 1; i <= n; ++i)

        {

            cost[i] = cost[i - 1] + sum[i - 1]*a[i];

            dp[i][0] = cost[i];//初始化

        }

       /* for (i = 1; i <= n; ++i) printf("%I64d ",cost[i]);

        puts("");*/

        ll ans = solve_DP();

        printf("%I64d\n",ans);

    }

    return 0;

}

 

 

 

你可能感兴趣的:(优化)