1. 程式人生 > >hdu2829之二維斜率優化DP

hdu2829之二維斜率優化DP

優先 rgb rst main top rec data order tel

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.

Input 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.
Output 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.
Sample Input
4 1
4 5 1 2
4 2
4 5 1 2
0 0

Sample Output
17
2
題意:n個數分成m段,每段的和是該段內的數兩兩之間的和,求最小值

/*分析:
假定dp[i][j]表示前i個數分成j段的最小值
cost[i]表示從1~i的數兩兩相乘的總和
sum[i]表示前i個數的和
則:
dp[i][j]=Min(dp[k][j-1]+cost[i]-cost[k]-sum[k]*(sum[i]-sum[k]))
=>dp[i][j]=dp[k][j-1]+cost[i]-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]
由於有sum[i]*sum[k]這一項,所以不可能用單調隊列維護
-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]的最小值
所以我們要把sum[i]獨立出來以便求維護單調隊列是和i無關 
如今我們須要找出最優的k,
令k2<k時k時最優的,即前k個數k為最優的取值
所以滿足:
dp[k][j-1]+cost[i]-cost[k]-sum[i]*sum[k]+sum[k]*sum[k]
<=dp[k2][j-1]+cost[i]-cost[k2]-sum[i]*sum[k2]+sum[k2]*sum[k2]
=>(dp[k][j-1]-cost[k]+sum[k]*sum[k]-(dp[k2][j-1]-cost[k2]+sum[k2]*sum[k2]))/(sum[k]-sum[k2])<=sum[i]
設:
y1=dp[k][j-1]-cost[k]+sum[k]*sum[k]
x1=sum[k]
y2=dp[k2][j-1]-cost[k2]+sum[k2]*sum[k2]
x2=sum[k2]
所以變成:
(y2-y1)/(x2-x1)
即兩點之間的斜率!
對於這個斜率我們怎麽來尋找關系維護?
假定k點之前k最優且k點在單調隊列的首位置 
如今對於k+1點
首先對於隊列中的點更新k+1之後的點能否比k,k+1..更優,即更新最長處(由於sum[k+1]添加了所以能夠更新最長處)
然後對於點k+1與隊列中最後一個點的斜率必須是整個隊列中斜率最大的,即保持斜率單調添加
為什麽?由於假如k+1與k的斜率比k與k-1的斜率更低,
則隨著sum[k+x]添加,k+1會首先更優於k,所以不須要k點,僅僅須要比較k+1與k-1的點的優先值 
*/
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <queue>
#include <algorithm>
#include <map>
#include <cmath>
#include <iomanip>
#define INF 99999999
typedef long long LL;
using namespace std;

const int MAX=1000+10;
int n,m,index,head,tail;
int s[MAX],q[MAX];
int dp[MAX][2],cost[MAX],sum[MAX];//dp採用滾動數組 

int GetY(int k,int k2){
	return dp[k][index^1]-cost[k]+sum[k]*sum[k]-(dp[k2][index^1]-cost[k2]+sum[k2]*sum[k2]);
}

int GetX(int k,int k2){
	return sum[k]-sum[k2];
}

void DP(){
	index=0;
	memset(dp,0,sizeof dp);
	for(int i=1;i<=n;++i)dp[i][index]=cost[i];
	for(int j=1;j<=m;++j){//分成j段,j作為第一層循環才用滾動數組
		index=index^1; 
		head=tail=0;
		q[tail++]=0;
		for(int i=1;i<=n;++i){
			while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*sum[i])++head;
			dp[i][index]=dp[q[head]][index^1]+cost[i]-cost[q[head]]-sum[i]*sum[q[head]]+sum[q[head]]*sum[q[head]];
			while(head+1<tail && GetY(i,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i,q[tail-1]))--tail;
			q[tail++]=i;
		}
	}
}

int main(){
	while(~scanf("%d%d",&n,&m),n+m){
		for(int i=1;i<=n;++i)scanf("%d",&s[i]);
		for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i];
		memset(cost,0,sizeof cost);
		for(int i=1;i<=n;++i){
			for(int j=i+1;j<=n;++j)cost[j]+=s[i]*s[j];
		}
		for(int i=1;i<=n;++i)cost[i]+=cost[i-1];
		DP();
		printf("%d\n",dp[n][index]);
	}
	return 0;
} 



hdu2829之二維斜率優化DP