[ACM]【区间DP】Atcoder163 Active Infants

Active Infants

题意:将一个数列重新排列,使得每个数字大小乘以移动距离的总和最大。
在这里插入图片描述

思路:

本垃圾一直纠结于小孩怎么交换,其实不应该纠结这里,而是应该想到要将小孩进行重新排序啊混蛋
很容易想到,应当尽可能使得小孩离原始位置越远。
其实,就等价于,使得每个小孩处于可能的最左边的位置或者最右边的位置。(比如小孩初始位置为4,他应当尽可能的移动到1或者n中离他远的位置)
而,小孩之间是不同的。那么应该以活跃值大的小孩作为优先,因为他对答案的贡献更大。所以首先进行活跃值降序。
依照上面的思路,就可以得出状态:
我们令dp[ i ][ j ]代表:新序列左边已经确定了i个位置,右边已经确定了j个位置。显然,现在面对的应该做出选择的小孩是按照活跃值降序的第i+j个小孩。
按照前文所述,这个小孩应当尽可能靠左或靠右。
状态转移方程:
//是否选择往最左边移动。
dp[l+1][r]=max(dp[l+1][r],dp[l][r]+child[i].act×abs(child[i].id-l);
//是否选择往最右边移动。
dp[l][r+1]=max(dp[l][r+1],dp[l][r]+child[i].act×abs(n-1-child[i].id-r);
同时更新ans。

代码:

#include<bits/stdc++.h>
using namespace std;
using ll=long long;
ll dp[2004][2004];
struct node{
	ll id,act;
}a[2004];
bool cmp(node a,node b){
	if(a.act!=b.act)return a.act>b.act;
	else return a.id<b.id;
}
int main(){
	ll n;
	scanf("%lld",&n);
	for(ll i=0;i<n;i++){
		scanf("%lld",&a[i].act);
		a[i].id=i;
	}
	sort(a,a+n,cmp);
	ll ans=0;
	//已经有多少个人放到了左边 
	for(int l=0;l<n;l++){
		//已经有多少个人放到了右边 
		for(int r=0;r<n;r++){
			//现在面对的是第几个人 
			int i=l+r;
			if(i>=n)break;
			
			ll act=a[i].act;
			ll loc=a[i].id;
			
			//是把现在这个人放到左边 
			dp[l+1][r]=max(dp[l+1][r],dp[l][r]+act*abs(l-loc));
			//还是把现在这个人放到右边 
			dp[l][r+1]=max(dp[l][r+1],dp[l][r]+act*abs((n-1)-loc-r));
			
			ans=max(ans,dp[l+1][r]);
			ans=max(ans,dp[l][r+1]);
		}
	}
	printf("%lld\n",ans);
}
发布了20 篇原创文章 · 获赞 1 · 访问量 579

猜你喜欢

转载自blog.csdn.net/weixin_45497996/article/details/105646145