【決策單調性分治優化/四邊形不等式優化】監獄警衛

前言

模板一套就AC了...

題目

guardians.cpp 1S/128M

你負責將監獄的警衛指派到最瘋狂的罪犯所在的監獄。 一共有N間牢房排列成一行,編號從1~N。 第i間牢房恰好容納了一個瘋狂程度爲C[i]的罪犯。

每個罪犯都應該有一個警衛監視他/她。 理想情況下,應該讓一名警衛監視一個罪犯。 然而,由於預算限制,你只能分配G個警衛。 爲了最大程度地降低有人逃脫的總風險,你必須指定每個警衛應該監視哪些罪犯。當然,你應該將每個警衛分配給一組相鄰的牢房。
第i個罪犯可能逃脫的風險R[i]由下式給出:
R[i] = C[i] * 指派監視他的警衛監視的罪犯數量
請你分配一個最佳的方案,使所有罪犯的風險之和最小。

輸入
第1行:2個整數N和G (1 <= N <= 8000, 1 <= G <= 800)
第2行:N個整數,表示C[i] ( 1 <= C[i] <= 10^9)

輸出
第1行:1個整數,表示答案

Sample Input
6 3
11
11
11
24
26
100

Sample Output
299

Explain
第1個警衛監視1~3,第2個警衛監視4~5,第3個警衛監視6

分析

詳細的關於該優化的講解我有寫博客:決策單調性分治優化

經觀察可以發現本題基本模型爲:分組+花費最小

可以寫出DP定義與轉移方程:

dp[ i ][ j ]:1~j個犯人被第i個獄長監管

對於最後一個獄長i,假設從第k到j的犯人被最後第i獄長監管

dp[ i ][ j ]=min( dp[ i ][ j ],dp[ i-1 ][ k-1 ]+( s[ j ] - s[ k ] ) * ( j - k + 1 ) ) ;                                                                                                   

s[ i ]:1~i的前綴和

初始化:dp[ 1 ][ 1~n ]=INF

然後直接套用【決策單調性分治優化】或【四邊形不等式優化】即可,詳見代碼

決策單調性分治優化-代碼(含暴力DP)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=8000,MAXG=800;
#define INF (1LL << 60) 
ll c[MAXN+5],s[MAXN+5],dp[MAXG+5][MAXN+5];
ll n,g;
void DP(ll d,ll i,ll j,ll optl,ll optr)//決策優化 
{
	if(i>j)
		return ;
	ll mid=(i+j)/2;
	ll opt=INF,id;
	//暴力計算二分點的dp值opt與最小決策點id 
	for(int k=optl;k<=min(mid,optr);k++)
	{
		ll cur=dp[d-1][k-1]+(mid-k+1)*(s[mid]-s[k-1]);
		if(cur<opt)
			opt=cur,id=k;
	}
	dp[d][mid]=opt;
	//遞歸求解決策點左右兩部分的dp值 
	DP(d,i,mid-1,optl,id);
	DP(d,mid+1,j,id,optr);
}
void Solve1()
{
	for(int i=1;i<=n;i++)
		dp[0][i]=INF;
	for(int i=1;i<=g;i++)
		DP(i,1,n,1,n);
	printf("%lld\n",dp[g][n]);
}
void Solve2()//普通DP(沒調出來,奇怪) 
{
	/*
	dp[i][j]:1~j個犯人被第i個獄長監管
	對於最後一個獄長i,假設從第k到j的犯人被最後第i獄長監管 
	dp[i][j]=min(dp[i][j],dp[i-1][k-1]+(s[j]-s[k])*(j-k+1)); 
	*/
	for(int i=1;i<=n;i++)
		dp[1][i]=INF;
	//dp[0][0]=0;
	for(int i=1;i<=g;i++)
		for(int j=1;j<=n;j++)
			for(int k=1;k<=j;k++)
			{
				//printf("(%d,%d):%lld\n",i,j,dp[i][j]);
				dp[i][j]=min(dp[i][j],dp[i-1][k-1]+(j-k+1)*(s[j]-s[k-1]));
				//printf(" %lld\n",dp[i][j]);
			}
	printf("%lld\n",dp[g][n]);
}
int main()
{
	//freopen("guardians.in","r",stdin);
	//freopen("guardians.out","w",stdout);
	scanf("%lld%lld",&n,&g);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		s[i]=s[i-1]+c[i];
	}
	Solve1();
	//Solve2();
	return 0;
} 

四邊形不等式優化-代碼

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int MAXN=8000,MAXG=800;
#define INF (1LL<<60) 
ll c[MAXN+5],s[MAXN+5],dp[MAXG+5][MAXN+5];
ll n,g;
void Solve()
{
	for(int i=0;i<=g;i++)
		for(int j=0;j<=n;j++)
			dp[i][j]=INF;
	dp[0][0]=0;
	for(int i=1;i<=g;i++)
	{
		ll opt=0;
		for(int j=i;j<=n;j++)
			for(int k=opt;k<=j;k++)
			{
				ll tmp=1ll*(j-k+1)*(s[j]-s[k-1]);
				if(dp[i-1][k-1]+tmp<=dp[i][j])
					dp[i][j]=dp[i-1][k-1]+tmp,opt=k;
			}
	}
	/*打表驗證單調性 
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++)
		{
			w[i][j]=(j-i+1)*(s[j]-s[i-1]);
			//printf("%d %d %lld\n",i,j,w[i][j]);
		}
	for(int a=1;a<=n;a++)
		for(int b=a+1;b<=n;b++)
			for(int c=b+1;c<=n;c++)
				for(int d=c+1;d<=n;d++)
					printf("%d %d %d %d %lld %lld\n",a,b,c,d,w[a][c]+w[b][d],w[a][d]+w[b][c]);
	*/
	printf("%lld\n",dp[g][n]);
}
int main()
{
	scanf("%lld%lld",&n,&g);
	for(int i=1;i<=n;i++)
	{
		scanf("%lld",&c[i]);
		s[i]=s[i-1]+c[i];
	}
	Solve();
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章