【POJ3666】【USACO 2008 Feb Gold】 2.Cow Game 动规

题意:有若干个数,然后可以花费i的代价让某个数+i或者-i。

现在要求让你把序列排成不升或者不降,问最小代价。


题解:

首先可以证明,最优花费下最后所有的数都可以是现在的某个数:


证:如果两个数调整后在两个数中间,那么可以把两个数都变为其中一个数,而代价显然是等同的。

这个出来后就好做了。

我们可以先离散化一下,然后f[i][j]表示第i个数变为j时1~i这些数保持非严格单调的最小花费

转移时f[i][j]不必n*n枚举,可以维护一个前缀最优(非常水),然后O(1)转移。


然后做一遍非升,一遍非降出解。


代码:

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define N 2500
#define inf 0x3f3f3f3f3f3f3f3fll
using namespace std;
struct LSH
{
	int x,id;
	bool operator < (const LSH &a)const{return x<a.x;}
}lsh[N];
int cnt,n,w[N];
long long f[N][N];
int main()
{
//	freopen("test.in","r",stdin);
	int i,j,k;
	scanf("%d",&n);
	for(i=1;i<=n;i++)scanf("%d",&lsh[i].x),lsh[i].id=i;
	sort(lsh+1,lsh+n+1),lsh[0].x=-1;
	for(i=1;i<=n;i++)
	{
		if(lsh[i].x!=lsh[i-1].x)lsh[++cnt].x=lsh[i].x;
		w[lsh[i].id]=cnt;
	}
	// 离散化结束,lsh中对应原值

//升序
	for(i=1;i<=n;i++)
	{
		long long temp=inf;
		for(j=0;j<=cnt;j++)
		{
			temp=min(temp,f[i-1][j]);
			f[i][j]=temp+abs(lsh[w[i]].x-lsh[j].x);
		}
	}
	long long ans1=inf;
	for(i=0;i<=cnt;i++)ans1=min(ans1,f[n][i]);
//降序
	for(i=1;i<=n;i++)
	{
		long long temp=inf;
		for(j=cnt;j>=0;j--)
		{
			temp=min(temp,f[i-1][j]);
			f[i][j]=temp+abs(lsh[w[i]].x-lsh[j].x);
		}
	}
	long long ans2=inf;
	for(i=0;i<=cnt;i++)ans2=min(ans2,f[n][i]);
	cout<<min(ans1,ans2)<<endl;
	return 0;
}


你可能感兴趣的:(USACO,2008,game,COW,Gold,Feb,动规,POJ3666)