dp斜率优化 hdu 2829 Lawrence 题解

来源:互联网 发布:淘宝网红模特有哪些 编辑:程序博客网 时间:2024/05/17 09:24

累加器传送门:

http://blog.csdn.net/NOIAu/article/details/71775000

题目传送门:

https://vjudge.net/problem/HDU-2829


题目:

T. E. Lawrence was a controversial figure during World War I. He was a British officer who served in the Arabian theater and led a group of Arab nationals in guerilla strikes against the Ottoman Empire. His primary targets were the railroads. A highly fictionalized version of his exploits was presented in the blockbuster movie, “Lawrence of Arabia”.

You are to write a program to help Lawrence figure out how to best use his limited resources. You have some information from British Intelligence. First, the rail line is completely linear—there are no branches, no spurs. Next, British Intelligence has assigned a Strategic Importance to each depot—an integer from 1 to 100. A depot is of no use on its own, it only has value if it is connected to other depots. The Strategic Value of the entire railroad is calculated by adding up the products of the Strategic Values for every pair of depots that are connected, directly or indirectly, by the rail line. Consider this railroad:
这里写图片描述

Its Strategic Value is 4* 5 + 4* 1 + 4* 2 + 5* 1 + 5* 2 + 1* 2 = 49.

Now, suppose that Lawrence only has enough resources for one attack. He cannot attack the depots themselves—they are too well defended. He must attack the rail line between depots, in the middle of the desert. Consider what would happen if Lawrence attacked this rail line right in the middle:
这里写图片描述

The Strategic Value of the remaining railroad is 4* 5 + 1* 2 = 22. But, suppose Lawrence attacks between the 4 and 5 depots:

这里写图片描述
The Strategic Value of the remaining railroad is 5* 1 + 5* 2 + 1*2 = 17. This is Lawrence’s best option.

Given a description of a railroad and the number of attacks that Lawrence can perform, figure out the smallest Strategic Value that he can achieve for that railroad.


输入:

There will be several data sets. Each data set will begin with a line with two integers, n and m. n is the number of depots on the railroad (1 ≤ n ≤ 1000), and m is the number of attacks Lawrence has resources for (0 ≤ m < n). On the next line will be n integers, each from 1 to 100, indicating the Strategic Value of each depot in order. End of input will be marked by a line with n=0 and m=0, which should not be processed.


输出:

For each data set, output a single integer, indicating the smallest Strategic Value for the railroad that Lawrence can achieve with his attacks. Output each integer in its own line.


样例输入:

4 1
4 5 1 2
4 2
4 5 1 2
0 0


样例输出:

17
2


题目梗概翻译:

n(1<=n<=1000)个数,将其分成m + 1 (0 <= m < n)组,要求每组数必须是连续的而且要求得到的价值最小。一组数的价值定义为该组内任意两个数乘积之和,如果某组中仅有一个数,那么该组数的价值为0


题目分析:

定义状态dp[i][j]表示前j点,分为i组的最小代价。

cost[i][j]表示i到j分为一组的代价

cnt[i]为前缀和


则有:
dp[i][j] = min(dp[k][j-1] + cost[k+1][i])
cost[1][i] = cost[1][k] + cost[k+1][i] + cnt[k] * (cnt[i]-cnt[k])
cost[k+1][i] = cost[1][i] - cost[1][k] - cnt[k]*(cnt[i]-cnt[k])
得出

dp[i][j]=dp[k][j-1]+cost[1][i]-cost[1][k]-cnt[k]*(cnt[i]-cnt[k]);

斜率优化即可

#include<iostream>#include<cstring>#include<cstdio>#define MAXN 1000+10using namespace std;int n,m;long long cnt[MAXN];long long cost[MAXN][MAXN];long long a[MAXN];long long dp[MAXN][MAXN];long long q[MAXN];long long sqr(long long a){ return a*a; }long long gety(int i,int k1,int k2){ return dp[i-1][k2]-dp[i-1][k1]+cost[1][k1]-cost[1][k2]+sqr(cnt[k2])-sqr(cnt[k1]);}long long getx(int k1,int k2){ return cnt[k2]-cnt[k1]; }int head,tail;void init(){    memset(cost,0,sizeof cost),cnt[0]=0;     for(register int i=1;i<=n;i++)scanf("%d",&a[i]);      for(register int i=1;i<=n;i++)cnt[i]=cnt[i-1]+a[i];      for(register int i=1;i<=n;i++)      for(register int j=i+1;j<=n;j++)    cost[i][j]=cost[i][j-1]+(cnt[j-1]-cnt[i-1])*a[j];}void dpp(){    for(register int i=0;i<=n;i++) dp[0][i]=cost[1][i];    for(register int i=1;i<=m;i++){        head=tail=0;        for(register int j=i;j<=n;j++){            while(head+1<tail&&gety(i,q[head],q[head+1])<=cnt[j]*getx(q[head],q[head+1])) head++;            if(head==tail) dp[i][j] = 0;            else dp[i][j]=dp[i-1][q[head]]+cost[q[head]+1][j];            while(head+1<tail&&gety(i,q[tail-2],q[tail-1])*1.0/getx(q[tail-2],q[tail-1])>=gety(i,q[tail-1],j)*1.0/getx(q[tail-1],j))            tail--;            q[tail++]=j;        }    }    cout<<dp[m][n]<<endl;}int main(){    while(scanf("%d%d",&n,&m),n+m){        init();        dpp();    }    return 0;}

这里写图片描述
注意斜率的转double即可,不然可能出错(之前都是用乘法,第一次用除法,乘法是不会出现精度错误的)

原创粉丝点击