1. 程式人生 > >斜率優化DP和四邊形不等式優化DP整理

斜率優化DP和四邊形不等式優化DP整理

當dp的狀態轉移方程dp[i]的狀態i需要從前面(0~i-1)個狀態找出最優子決策做轉移時 我們常常需要雙重迴圈

(一重迴圈跑狀態 i,一重迴圈跑 i 的所有子狀態)這樣的時間複雜度是O(N^2)而 斜率優化或者四邊形不等式優化後的DP

可以將時間複雜度縮減到O(N)

O(N^2)可以優化到O(N) ,O(N^3)可以優化到O(N^2),依次類推

斜率優化DP和四邊形不等式優化DP主要的原理就是利用斜率或者四邊形不等式等數學方法

在所有要判斷的子狀態中迅速做出判斷,所以這裡的優化其實是省去了列舉i的子狀態的迴圈,幾乎就是直接把最優的子狀態找出來了

其中四邊形不等式優化是用陣列s邊跑邊求最優的子狀態,例如用s[i][j]儲存dp[i][j]的最優子狀態

斜率優化的話是將後面可能用到的子狀態放到佇列中,要求的當前狀態的最優狀態就是隊首元素q[head]

另外,網上見到很多用二分+DP解斜率優化的問題。

以dp求最小值為例:

主要的解題步驟就是先寫出dp的狀態轉移方程,然後選取兩個子狀態p,q

假設p < q而決策q比p更好,求出斜率不等式,然後就可以寫了

至於經常有題目控制子決策的範圍什麼的(比如控制區間長度,或者控制分組的組數),就需要具體情況具體分析

最最最簡單的斜率DP優化的題,就算不用優化,O(N^2)的演算法也可以AC

這題絕壁是最最最適合入門的斜率DP的題,我發誓!!!

版本一:(O(N^2))

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
	dp[i]表示買前i種珍珠的最少花費
	
	dp[i] = min(dp[j] + (sum[i] - sum[j] + 10)*w[i])
	其中sum[i]-sum[j]表示第j+1種珍珠到第i種珍珠所需的數量
	w[i]表示第i種珍珠的價值
	 
*/
const int N = 111;
int w[N],dp[N],sum[N];
int main()
{
    int T;Sint(T);
    while (T--)
    {
    	int n;Sint(n);
    	for (int i = 1,x;i <= n;++i)
    	{
    		Sint2(x,w[i]);
    		sum[i] = sum[i-1] + x;
		}
		dp[1] = (sum[1]+10)*(w[1]);
		for (int i = 2;i <= n;++i)
		{
			dp[i] = dp[i-1] + (sum[i]-sum[i-1]+10)*w[i];
			for (int j = 0;j < i-1;++j)
			{
				dp[i] = min(dp[i],dp[j] + (sum[i]-sum[j]+10)*w[i]);
			}
		}
		Pintc(dp[n],'\n');
	}
    return 0;
}
當做出暴力DP版本之後,只需再多考慮一步就可以變成斜率優化DP

對於狀態轉移方程dp[i] = dp[j] + (sum[i]-sum[j]+10)*w[i]

考慮 k < j < i 且假設 i狀態由j狀態轉移得到比由k狀態轉移得到更優

即:dp[j] + (sum[i]-sum[j]+10)*w[i] <= dp[k] + (sum[i] - sum[k] + 10)*w[i]

(這裡取小於號是因為dp儲存的是最小花費,花費越小越好,取等是因為j比k大,所以就算k,j一樣優也選j)

這個不等式化簡之後就是

dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])

這裡的w[i]滿足單調遞增

有了上面的不等式和單調條件就可以斜率優化了,主要做法就是利用單調佇列維護滿足的點

比如j狀態優於k狀態,就可以將k永遠的剔除了

具體對於子狀態的維護見程式碼裡面有2個對佇列進行的刪除的操作,一個是在求dp[i]時在隊首刪除

一個是在將狀態i加入佇列時在隊尾刪除的操作

版本二:(O(N))

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
	dp[i]表示買前i種珍珠的最少花費
	
	dp[i] = min(dp[j] + (sum[i] - sum[j] + 10)*w[i])
	其中sum[i]-sum[j]表示第j+1種珍珠到第i種珍珠所需的數量
	w[i]表示第i種珍珠的價值
	
	dp[j] - dp[k] <= w[i]*(sum[j]-sum[k])
	The qualities of the classes (and so the prices) are given in ascending order.
	So w[i]單增 --斜率DP 
*/
const int N = 111;
int w[N],dp[N],sum[N];
int q[N];
int DP(int i,int j)
{
	return dp[j] + (sum[i]-sum[j]+10)*w[i];
}
int dy(int i,int j)
{
	return dp[i]-dp[j];
}
int dx(int i,int j)
{
	return sum[i]-sum[j];
}
int main()
{
    int T;Sint(T);
    while (T--)
    {
    	int n;Sint(n);
    	for (int i = 1,x;i <= n;++i)
    	{
    		Sint2(x,w[i]);
    		sum[i] = sum[i-1] + x;
	}
		int head = 0,tail = 0;
		q[tail++] = 0;
		for (int i = 1;i <= n;++i)
		{
			while (head+1<tail&&dy(q[head+1],q[head])<=w[i]*dx(q[head+1],q[head])) ++head;
			dp[i] = DP(i,q[head]);
			while (head+1<tail&&dy(i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail;
			q[tail++] = i;
		}
		Pintc(dp[n],'\n');
    }
    return 0;
}

列狀態轉移方程 然後假設 k < j < i 且j決策更好 不等式列出來就好了
#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int N = 500007;
int dp[N];
int sum[N];
int q[N];
int n,m;
int EX(int x)
{
	return x*x;
}
int getDP(int i,int j)
{
	return dp[j] + m + EX(sum[i]-sum[j]);
}
int getUP(int j,int k)//yj - yk 
{
	return (dp[j] + EX(sum[j])) - (dp[k] + EX(sum[k]));
}
int getDown(int j,int k)//xj - xk
{
	return 2*(sum[j] - sum[k]);
}
int main()
{
	
    while (Sint2(n,m) == 2)
    {
    	for (int i = 1,x;i <= n;++i)
    	{
    		Sint(x);
    		sum[i] = sum[i-1] + x;
		}
		int head = 0,tail = 0;
		q[tail++] = 0;//單調佇列 (單增) 
		for (int i = 1;i <= n;++i)
		{
			//                  getup/getdown  <= sum[i]
			while (head+1<tail&&getUP(q[head+1],q[head])<=sum[i]*getDown(q[head+1],q[head])) head++;
			dp[i] = getDP(i,q[head]);
			//          getup(i,q[tail-1])/getdown(i,q[tail-1]) <= getup(q[tail-1],q[tail-2])/getdown(q[tail-1],q[tail-2])
			while (head+1<tail&&getUP(i,q[tail-1])*getDown(q[tail-1],q[tail-2])<=getUP(q[tail-1],q[tail-2])*getDown(i,q[tail-1]))tail--;
			q[tail++] = i;
		}
		Pintc(dp[n],'\n');
	}
    return 0;
}

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Schars(s) scanf("%s",s) 
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
	dp[i][j]表示前j個數分成i組的最小价值
	sum[i]表示前i個數的和
	cost[i]表示前i個數的花費 
*/
const int N = 1004;
int sum[N],cost[N],dp[N][N];
int q[N],head,tail;
int n,m;
int EX(int x)
{
	return x*x;
}
int dy(int x,int j,int i)
{
	return dp[x][i] - cost[i] + EX(sum[i]) - (dp[x][j] - cost[j] + EX(sum[j]));
}
int dx(int j,int i)
{
	return sum[i] - sum[j];
}
int DP(int x,int j,int i)
{
	return dp[x][i]+cost[j] - cost[i] - sum[i]*(sum[j]-sum[i]);
}
int main()
{
    while (Sint2(n,m)==2&&(n||m))
    {
    	++m;
    	for (int i = 1,x;i <= n;++i)
    	{
    		Sint(x);
    		sum[i] = sum[i-1] + x;
    		cost[i] = cost[i-1] + sum[i-1]*x;
		}
		for (int i = 1;i <= n;++i) dp[1][i] = cost[i];
		for(int i = 2;i <= m;++i)
		{
			head = tail = 0;
			q[tail++] = i-1;
			for (int j = i;j <= n;++j)
			{
				while (head+1<tail&&dy(i-1,q[head],q[head+1])<=sum[j]*dx(q[head],q[head+1])) head++;
				dp[i][j] = DP(i-1,j,q[head]);
				while (head+1<tail&&dx(q[tail-2],q[tail-1])*dy(i-1,q[tail-1],j)<=dy(i-1,q[tail-2],q[tail-1])*dx(q[tail-1],j)) --tail;
				q[tail++] = j;
			}
		}
		Pintc(dp[m][n],'\n');
	}
    return 0;
}


這題、、、

如果現在有人能過的話請聯絡博主 ,不勝感基、、、

貪心處理下

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%lld",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%lld %lld",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%lld%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
	dp[i][j]表示 前i個人挖j個洞的最小花費
	1.當w[i] <= w[j] && h[i]<=h[j]時 捨棄 (w[i],h[i])
	2.將人按w遞增 h遞減 排序,即滿足 w[j] < w[i]&&h[j] > h[i]
	
	故dp[i][j] = dp[k][j-1] + w[i]*h[k+1]
	 
*/
const int N = 500007;

struct Node
{
	ll h,w;
}b[N];
int q[N],head,tail;
bool cmp(Node a,Node b)
{
	if (a.h == b.h) return a.w > b.w;
	return a.h > b.h;//確保h遞減 
}
ll dp[N][104];
ll dy(int j,int k,int t)
{
	return dp[j][t] - dp[k][t];
}
ll dx(int j,int k)
{
	return b[k+1].h - b[j+1].h;
}
int main()
{
    int n,k;
    while (Sint2(n,k) == 2)
    {
    	for (int i = 1;i <= n;++i)
    	{
    		Sll2(b[i].w,b[i].h);
		}
		sort(b+1,b+n+1,cmp);
		int t = 1;
		for (int i = 1;i <= n;++i)
		{
			if (b[t].w < b[i].w) b[++t] = b[i];
		}
//		cout<<t<<endl; 
		k = min(t,k);
		for (int i = 1;i <= t;++i) dp[i][1] = b[i].w*b[1].h;
		for (int j = 2;j <= k;++j)
		{
			head = tail = 0;mem(q,0);
			q[tail++] = 0;
			for (int i = 1;i <= t;++i)
			{
				while (head+1<tail&&dy(q[head+1],q[head],j-1) <= b[i].w * dx(q[head+1],q[head])) ++head;
				dp[i][j] = dp[q[head]][j-1] + b[i].w * b[q[head]+1].h;
				while (head+1<tail&&dy(i,q[tail-1],j-1)*dx(q[tail-1],q[tail-2]) <= dy(q[tail-1],q[tail-2],j-1)*dx(i,q[tail-1])) --tail;
				q[tail++] = i;
			}
		}
		ll ans = dp[t][1];
		for (int i = 2;i <= k;++i)
		{
			ans = min(ans,dp[t][i]);
		}
		Pllc(ans,'\n');
    }
    return 0;
}


#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
Cows in the same team should reduce their Moo~ to the one who has the lowest Moo~ in this team

	dp[i]表示前i頭牛的最小花費
	dp[i] = dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1]) 
	dp[j]-dp[k]+sum[k]-sum[j]+j*a[j+1]-k*a[k+1] < i*(a[j+1]-a[k+1])
*/
const int N = 400004;
ll dp[N],a[N],sum[N];
int q[N],head,tail;
ll dy(int j,int k)
{
	return dp[j]-dp[k] + sum[k]-sum[j] + j*a[j+1]-k*a[k+1];
}
ll dx(int j,int k)
{
	return a[j+1] - a[k+1];
}
ll DP(int i,int j)
{
	return dp[j] + (sum[i]-sum[j]-(i-j)*a[j+1]);
}
int main()
{
    int n,t;
    while (Sint2(n,t) == 2)
    {
    	for (int i = 1;i <= n;++i) Sll(a[i]);
    	sort(a+1,a+n+1);
    	for (int i = 1;i <= n;++i) sum[i] = sum[i-1] + a[i];
		head = tail = 0;
		q[tail++] = 0;
		for (int i = 1;i <= n;++i)
		{
			while (head+1<tail&&dy(q[head+1],q[head]) <= i*dx(q[head+1],q[head])) ++head;
			dp[i] = DP(i,q[head]);
			int j = i-t+1;
			if (j < t) continue;
			while (head+1<tail&&dy(j,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(j,q[tail-1])) --tail;
			q[tail++] = j;
		}
		Pllc(dp[n],'\n');
	}
    return 0;
}

斜率DP寫的怎麼都過不了,最後用四邊形不等式,要保證j-i遞增於是列舉長度

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
/*
	dp[i][j]表示從i到j所需的最小花費
	dp[i][j] = min {dp[i][k] + dp[k+1][j] + w(i,k,j)}
	w(i,k,j) = y[k] - y[j] + x[k+1] - x[i]
	s[i][j] = k 表示 dp[i][j]這個狀態最優的決策是 k 
*/
const int N = 1003;
const int inf = 0x3f3f3f3f;
int dp[N][N],s[N][N];
int x[N],y[N];
int w(int i,int k,int j)
{
	if (k >= j) return inf;
	return y[k] - y[j] + x[k+1] - x[i];
}
int DP(const int &n)
{
	mem(dp,0);int tmp;
	for (int L = 2;L<=n;++L) //以j-i遞增為順序遞推
	{
		for (int i = 1,j = L;i+L-1<=n;++i,j = i+L-1)//i 是 區間左端點,j是區間右端點
		{
			dp[i][j] = inf;
			for (int k = s[i][j-1];k <= s[i+1][j];++k)
			{
				tmp = dp[i][k] + dp[k+1][j] + w(i,k,j);
				if (tmp < dp[i][j])
				{
					dp[i][j] = tmp;
					s[i][j] = k;
				}
			}
		} 
	} 
	return dp[1][n];
}
int main()
{
    int n;
    while (Sint(n) == 1)
    {
    	for (int i = 1;i <= n;++i) 
		{
			Sint2(x[i],y[i]);
			s[i][i] = i;
		}
		Pintc(DP(n),'\n');
	}
    return 0;
}

狀態轉移方程寫的好的話不用優化也可以過 

這個題的狀態轉移的方程很經典啊

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s)
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
    dp[i][j]表示在前i個村莊建j個郵局的最小值
    d[i][j]表示在[i,j]區間建1個郵局的最小值
    dp[i][j] = min{dp[k][j-1] + d[k+1][j]} (1<=k<j)
*/
const int N = 304;
int V,P;
int x[N];
int dp[N][33];
int d[N][N];
int DP()
{
    for (int i = 1;i <= V;++i)
    {
        for (int j = i+1;j <= V;++j)
        {
            d[i][j] = d[i][j-1] + x[j] - x[(i+j)/2];
        }
    }
    for (int i = 1;i <= V;++i) dp[i][0] = inf;
    for (int i = 1;i <= V;++i)
    {
        for (int j = 1;j <= min(i,P);++j)
        {
//            if (j > P) break;
            dp[i][j] = inf;
            for (int k = j-1;k < i;++k)
            {
                dp[i][j] = min(dp[i][j],dp[k][j-1]+d[k+1][i]);
            }
        }
    }
    return dp[V][P];
}
int main()
{
    while (Sint2(V,P) == 2)
    {
        for (int i = 1;i <= V;++i) Sint(x[i]);
        sort(x+1,x+V+1);
        Pintc(DP(),'\n');
    }
    return 0;
}

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	dp[i]表示前i個job的最小花費
	dp[i] = dp[j] + (S+sumt[i] - sumt[j]) *(sumf[i]-sumf[j])
*/ 
const int N = 10004;
ll dp[N];
ll sumt[N],sumf[N];
ll t[N],f[N]; 
int S,n;
int q[N],head,tail;
ll dy(int j,int k)
{
	return dp[j] - dp[k];
}
ll dx(int j,int k)
{
	return sumt[j] - sumt[k];
}
ll DP(int i,int j)
{
	return dp[j] + (S + sumt[i]-sumt[j])*sumf[i];
}
int main()
{
    while (Sint2(n,S) == 2)
    {
    	for (int i = n;i >= 1;--i) Sll2(t[i],f[i]);
    	for (int i = 1;i <= n;++i)
    	{
    		sumt[i] = sumt[i-1] + t[i];
    		sumf[i] = sumf[i-1] + f[i];
		}
		head = tail = 0;
		q[tail++] = 0;
		for (int i = 1;i <= n;++i)
		{
			while (head+1<tail&&dy(q[head],q[head+1])>dx(q[head],q[head+1])*sumf[i]) ++head;
			dp[i] = DP(i,q[head]);
			while (head+1<tail&&dy(q[tail-1],i)*dx(q[tail-2],q[tail-1])<dy(q[tail-2],q[tail-1])*dx(q[tail-1],i)) --tail;
			q[tail++] = i;
		}
		Pllc(dp[n],'\n');
	}
    return 0;
}

二分做的。。。。

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sdb(n) scanf("%lf",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	雖然據說 斜率DP 更牛逼
	雖然據說 二分很 low
	。。。 。。。 
*/
const int N = 100004;
const double esp = 1e-7;
double a[N],sum[N];
int n,f;//n塊地至少分f分
bool ok(double mid)
{
	double div = sum[f-1] - (f-1)*mid;
	for (int i = f;i <= n;++i)
	{
		div += a[i] - mid;
		div = max(div,sum[i]-sum[i-f]-f*mid);
		if (div > -esp) return 1;
	}
	return 0;
} 
int main()
{
    while (Sint2(n,f) == 2)
    {
    	double l = inf,r = 0;
    	for (int i = 1;i <= n;++i)
    	{
    		Sdb(a[i]);
    		sum[i] = sum[i-1] + a[i];
    		l = min(l,a[i]);
    		r = max(r,a[i]);
		}
		while (r-l>=esp)
		{
			double mid = (l+r)/2.0;
			if (ok(mid)) l = mid;
			else r = mid;
		}
		Pintc((int)(r*1000),'\n');
	}
    return 0;
}

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	dp[i]表示前i個數的最小值
	dp[i] = dp[j] + (sum[i]-sum[j]) - (i-j)*a[j+1] 
	
	dp[j]-dp[k] - (sum[j]-sum[k]) + j*a[j+1] - k*a[k+1]
	< i*(a[j+1]-a[k+1]) 
*/
const int N = 500004;
ll dp[N];
ll sum[N];
ll a[N];
int q[N],head,tail;
int n,k;
ll dy(int j,int k)
{
	return dp[j]-dp[k] - (sum[j]-sum[k]) + j*a[j+1] - k*a[k+1];
}
ll  dx(int j,int k)
{
	return a[j+1] - a[k+1];
}
ll DP(int i,int j)
{
	
	return dp[j] + (sum[i]-sum[j]) - (i-j)*a[j+1];
}
int main()
{
    int T;Sint(T);
    while (T--)
    {
    	Sint2(n,k);
    	for (int i = 1;i <= n;++i)
    	{
    		Sll(a[i]);
    		sum[i] = sum[i-1] + a[i];
		}
    	head = tail = 0;
    	q[tail++] = 0;
    	for (int i = k;i <= n;++i)
    	{
    		while (head+1<tail&&dy(q[head+1],q[head])<=i*dx(q[head+1],q[head])) ++head;
    		dp[i] = DP(i,q[head]);
//    		cout<<"head = "<<q[head]<<" : ";
//    		printf("dp[%d] = %d\n",i,dp[i]);
    		int j = i - k + 1;
    		if (j < k) continue;
			while (head+1<tail&&dy(j,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(q[tail-1],q[tail-2])*dx(j,q[tail-1])) --tail;
			q[tail++] = j; 
		}
		Pllc(dp[n],'\n');
	}
    return 0;
}


13.UVA 12594 Naming Babies (找不到連結額。。。)

和第9題一樣的想法  但是存不下  換了個方法

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	dp[i][j]表示前i個字元分成j份的最小值
	d[i][j]表示 [i,j] 當成一份的花費 
	dp[i][j] = dp[k][j-1] + d[k+1][j]
	but d[20000][20000]開不下。。。
	so 換個方法算d[i][j]
	sum1[i]表示前i個數的和
	sum2[i]表示 求和((i-1)*a[i])
	sum3[i]表示 求和(a[i]*a[i])
	故 
	dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k])
	 
*/
const int N = 20004;
ll dp[N][504],sum1[N],sum2[N],sum3[N];
int a[N];
int mp[30];
char NP[30];
char NM[N];
int q[N],head,tail;
int K;
int n;
void init()
{
	Schars(NP);Sint(K);
	Schars(NM);
	for (int i = 0;i < strlen(NP);++i)
	{
		mp[NP[i] - 'a'] = i; 
	} 
	n = strlen(NM);
	for (int i = 0;i < n;++i)
	{
		a[i+1] = mp[NM[i] - 'a'];
	}
	for (int i = 1;i <= n;++i)
	{
		sum1[i] = sum1[i-1] + a[i];
		sum2[i] = sum2[i-1] + (i-1)*a[i];
		sum3[i] = sum3[i-1] + a[i]*a[i];
	}
}
//dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k])
ll DP(int i,int j,int k)
{
	return dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i] - sum1[k]) - (sum3[i] - sum3[k]);
}
ll dy(int j,int q,int p)
{
	return dp[q][j-1]-dp[p][j-1] + q*sum1[q] - p*sum1[p] - (sum2[q]-sum2[p]) + (sum3[q]-sum3[p]);
}
ll dx(int q,int p)
{
	return q-p;
}
ll solve()
{
	for (int i = 1;i <= n;++i) dp[i][1] = sum2[i] - sum3[i];
	for (int j = 2;j <= K;++j)
	{
		head = tail = 0;
		q[tail++] = 0;
		for (int i = 1;i <= n;++i)
		{
			while (head+1<tail&&dy(j,q[head+1],q[head])<=sum1[i]*dx(q[head+1],q[head])) ++head;
			dp[i][j] = DP(i,j,q[head]);
			while (head+1<tail&&dy(j,i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(j,q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail;
			q[tail++] = i;
		}
	} 
	return dp[n][K];
}
int kas; 
int main()
{
    int T;Sint(T);
    while (T--)
    {
    	init();
    	printf("Case %d: %lld\n",++kas,solve());
	}
    return 0;
}

#define mem(a,x) memset(a,x,sizeof(a))
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<stack>
#include<cmath>
#include<map>
#include<stdlib.h>
#include<cctype>
#include<string>
#define Sint(n) scanf("%d",&n)
#define Sll(n) scanf("%I64d",&n)
#define Schar(n) scanf("%c",&n)
#define Schars(s) scanf("%s",s) 
#define Sint2(x,y) scanf("%d %d",&x,&y)
#define Sll2(x,y) scanf("%I64d %I64d",&x,&y)
#define Pint(x) printf("%d",x)
#define Pllc(x,c) printf("%I64d%c",x,c)
#define Pintc(x,c) printf("%d%c",x,c)
using namespace std;
typedef long long ll;
const int inf = 0x3f3f3f3f;
/*
	dp[i][j]表示前i個字元分成j份的最小值
	d[i][j]表示 [i,j] 當成一份的花費 
	dp[i][j] = dp[k][j-1] + d[k+1][j]
	but d[20000][20000]開不下。。。
	so 換個方法算d[i][j]
	sum1[i]表示前i個數的和
	sum2[i]表示 求和((i-1)*a[i])
	sum3[i]表示 求和(a[i]*a[i])
	故 
	dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k])
	 
*/
const int N = 20004;
ll dp[N][504],sum1[N],sum2[N],sum3[N];
int a[N];
int mp[30];
char NP[30];
char NM[N];
int q[N],head,tail;
int K;
int n;
void init()
{
	Schars(NP);Sint(K);
	Schars(NM);
	for (int i = 0;i < strlen(NP);++i)
	{
		mp[NP[i] - 'a'] = i; 
	} 
	n = strlen(NM);
	for (int i = 0;i < n;++i)
	{
		a[i+1] = mp[NM[i] - 'a'];
	}
	for (int i = 1;i <= n;++i)
	{
		sum1[i] = sum1[i-1] + a[i];
		sum2[i] = sum2[i-1] + (i-1)*a[i];
		sum3[i] = sum3[i-1] + a[i]*a[i];
	}
}
//dp[i][j] = dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i]-sum1[k]) - (sum3[i] - sum3[k])
ll DP(int i,int j,int k)
{
	return dp[k][j-1] + sum2[i] - sum2[k] - k*(sum1[i] - sum1[k]) - (sum3[i] - sum3[k]);
}
ll dy(int j,int q,int p)
{
	return dp[q][j-1]-dp[p][j-1] + q*sum1[q] - p*sum1[p] - (sum2[q]-sum2[p]) + (sum3[q]-sum3[p]);
}
ll dx(int q,int p)
{
	return q-p;
}
ll solve()
{
	for (int i = 1;i <= n;++i) dp[i][1] = sum2[i] - sum3[i];
	for (int j = 2;j <= K;++j)
	{
		head = tail = 0;
		q[tail++] = 0;
		for (int i = 1;i <= n;++i)
		{
			while (head+1<tail&&dy(j,q[head+1],q[head])<=sum1[i]*dx(q[head+1],q[head])) ++head;
			dp[i][j] = DP(i,j,q[head]);
			while (head+1<tail&&dy(j,i,q[tail-1])*dx(q[tail-1],q[tail-2])<=dy(j,q[tail-1],q[tail-2])*dx(i,q[tail-1])) --tail;
			q[tail++] = i;
		}
	} 
	return dp[n][K];
}
int kas; 
int main()
{
    int T;Sint(T);
    while (T--)
    {
    	init();
    	printf("Case %d: %lld\n",++kas,solve());
	}
    return 0;
}