【USACO 2013 March Gold】奶牛逃跑

Description

农夫约翰的牧场围栏上出现了一个洞,有N(1 <= N <= 1,000)只牛从这个洞逃出了牧场。这些出逃的奶牛很狂躁,他们在外面到处搞破坏,每分钟每头牛都会给约翰带来1美元的损失。约翰必须用缰绳套住所有的牛,以停止他们搞破坏。 

幸运的是,奶牛们都在牧场外一条笔直的公路上,牧场的大门恰好位于公里的0点处。约翰知道每头牛距离牧场大门的距离P_i(-500,000 <= P_i <= 500,000, P_i != 0) 

约翰从农场大门出发,每分钟移动一个单位距离,每到一头牛所在的地点,约翰就会给它套上缰绳,套缰绳不花时间。按怎样的顺序去给牛套缰绳才能使约翰损失的费用最少? 

Input

第一行,1个整数N 
接下来N行,每行一个整数,表示每头牛的位置

Output

一个整数,表示所求答案

Sample Input

4
-2
-12
3
7

Sample Output

50


【分析】

    显然是DP。我们记录状态f[i][j][k]表示从起点开始,左边套了i头牛,右边套了j头牛,当前位置在k(0表示在最左边,1表示在最右边)的最优值。

    处理的时候,我用数组A[]记录:在起点左边的牛离起点的距离,从小到大排序。有n个元素。

                    数组B[]记录:在起点右边的牛离起点的距离,从小到大排序。有m个元素。

    于是,我们得到状态转移方程:

    f[i][j][0]=min{

                    f[i-1][j][0]+(A[i]-A[i-1])*(m-j+n-(i-1)),

                    f[i-1][j][1]+(B[j]+A[i])*(m-j+n-(i-1))

                    }

    f[i][j][1]=min{

                    f[i][j-1][0]+(B[j]+A[i])*(n-i+m-(j-1)),

                    f[i][j-1][1]+(B[j]-B[j-1])*(n-i+m-(j-1))

                    }

    最后min(f[n][m][0],f[n][m][1])即为答案。


【代码】

/************************
    ID:Ciocio
	LANG:C++
	DATE:2013-12-21
	TASK:Escape
************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

#define MAXN 1005

int N,n,m;
int A[MAXN],B[MAXN],f[MAXN][MAXN][2];

void _read(int &x,bool mark=0)
{
	char tt=getchar();
	while(tt<'0'||'9'<tt){if(tt=='-') mark=1;tt=getchar();}
	for(x=0;'0'<=tt&&tt<='9';x=(x<<3)+(x<<1)+tt-'0',tt=getchar());
	x=mark?-x:x;
}

void _init()
{
	_read(N);
	for(int i=1,x;i<=N;i++)
	{
		_read(x);
		if(x<0) A[++n]=-x;   //在左边,放入A[]数组
		if(x>0) B[++m]=x;    //在右边,放入B[]数组
	}
}

void _solve()
{
	sort(A+1,A+n+1);
	sort(B+1,B+m+1);
	for(int i=1;i<=n;i++)       //边界
	{
		f[i][0][0]=f[i-1][0][0]+(A[i]-A[i-1])*(m+n-(i-1));
		f[i][0][1]=f[i][0][0]+A[i]*(m+n-(i-1));
	}
	for(int i=1;i<=m;i++)
	{
		f[0][i][1]=f[0][i-1][1]+(B[i]-B[i-1])*(n+m-(i-1));
		f[0][i][0]=f[0][i][1]+B[i]*(n+m-(i-1));
	}
	for(int i=1;i<=n;i++)          //DP
		for(int j=1;j<=m;j++)
		{
			f[i][j][0]=min(
				f[i-1][j][0]+(A[i]-A[i-1])*(m-j+n-(i-1)),
			    f[i-1][j][1]+(B[j]+A[i])*(m-j+n-(i-1))
			    );
			f[i][j][1]=min(
			    f[i][j-1][0]+(B[j]+A[i])*(n-i+m-(j-1)),
			    f[i][j-1][1]+(B[j]-B[j-1])*(n-i+m-(j-1))
			    );
		}
	cout<<min(f[n][m][1],f[n][m][0])<<endl;
}

int main()
{
	_init();
	_solve();
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值