BZOJ4586: [Usaco2016 Open]Landscaping

Description

农夫约翰正在建造一个美丽的花园,在这个过程中需要移动大量的泥土。花园由N个花圃(1≤N≤100,000)组成,
第i个花圃最开始有Ai个泥土。 农夫约翰想要重新整理花园,使每个花圃最后有Bi个泥土。Ai和Bi都是0...10范围
内的整数。为了整理花园,Farmer John有几个选择:他可以购买一个单位的泥土,并将它放在他选择的花圃中,
用X单位的钱。 他可以从他选择的花圃上清除一块泥土,并用Y单位的钱运出去。他还可以用Z*|i-j|的花费将一单
位的泥土从花圃i运输到花圃j。请计算农民约翰完成他的绿化项目的最低总成本。

Input

第一行输入包含N,X,Y和Z(0≤X,Y≤10^8; 0≤Z≤1000)。
行i + 1包含整数Ai和Bi。

Output

请输出FJ需要花在园林绿化上的最低总成本。

Sample Input

4 100 200 1
1 4
2 3
3 2
4 0

Sample Output

210

HINT

Source

DP
首先一个naive的dp是用min edit distance,这个是O((10n)^2)的
还是考虑DP
首先我们把序列写成若干个±1的形式
然后就可以理解为有一堆±1配对或移除,问最小代价
有3个性质:
第一:匹配的±1一定不会相交
显然,把它们改成包含一定更优
第二:可以分层DP
把每一层都写成±1交替的形式
这个的证明,如果有一段和>0,那么我们一定要移除其中的一个1,那么我们平移某个端点,使得被移除的1在端点处不会更劣,<0同理
第三:对于每一层的匹配,两个位置i,j配对的花费是min(x + y, z * dis)我们把x + y更小的(i,j)定义为长区间,那么存在包含关系的一定是长区间包含若干个短区间
这个证明很容易,分类讨论,容易发现如果长包含长或者短包含短可以通过调整变得更优
然后就可以DP了
#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define fill( x, y ) memset( x, y, sizeof x )
#define copy( x, y ) memcpy( x, y, sizeof x )
using namespace std;

typedef long long LL;
typedef pair < int, int > pa;

const int MAXN = 100010;
const LL INF = 1e16;

int n, x, y, z, cnt, type[MAXN];
LL f[MAXN], g[MAXN], ans;
map < int, int > m;
vector < int > v[MAXN];

inline void dp(vector < int > &v, LL *f)
{
	LL sum1 = 0, sum2 = 0, mn = INF;
	int n = v.size();
	for( int i = 1, j = 0 ; i < n ; i += 2 )
	{
		if( i > 1 ) sum1 += 1LL * z * abs( v[ i - 1 ] - v[ i - 2 ] ), mn += 1LL * z * abs( v[ i - 1 ] - v [ i - 2 ] );
		while( j < i && x + y < 1LL * z * abs( v[ i ] - v[ j ] ) )
		{
			if( j ) sum2 += 1LL * z * abs( v[ j ] - v[ j - 1 ] );
			mn = min( mn, sum1 - sum2 + x + y + ( j ? f[ j - 1 ] : 0 ) );
			j += 2;
		}
		f[ i ] = min( mn, 1LL * z * abs( v[ i ] - v[ i - 1 ] ) + f[ i - 2 ] );
	}	
}

inline LL solve(vector < int > &v, int c)
{
	int n = v.size(); LL ret = 0;
	if( n == 1 ) return c;
	dp( v, f );
	if( !( n & 1 ) ) return f[ n - 1 ];
	reverse( v.begin(), v.end() );
	dp( v, g );
	reverse( g, g + n );
	ret = c + min( f[ n - 2 ], g[ 1 ] );
	for( int i = 1 ; i < n - 2 ; i += 2 ) ret = min( ret, f[ i ] + g[ i + 2 ] + c );
	return ret;
}

int main()
{
#ifdef wxh010910
	freopen( "data.in", "r", stdin );
#endif
	scanf( "%d%d%d%d", &n, &x, &y, &z );
	for( int i = 1, sum = 0, last = 0 ; i <= n ; i++ )
	{
		int a, b, d, len;
		scanf( "%d%d", &a, &b );
		if( a == b ) continue;
		d = ( a - b > 0 ) ? 1 : -1;
		len = max( a - b, b - a );
		while( len-- )
		{
			if( d == last ) sum += d;
			if( m.find( sum ) == m.end() ) type[ m[ sum ] = ++cnt ] = d;
			v[ m[ sum ] ].pb( i );
			last = d;
		}
	}
	for( int i = 1 ; i <= cnt ; i++ ) ans += solve( v[ i ], type[ i ] > 0 ? y : x ); 
	cout << ans << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值