题目来源:(Educational Codeforces Round 109 (Rated for Div. 2))
题意:
有n个座位,上面坐了至多n/2向下取整的人
定义移动一个人从i座位至j座位的代价为abs(i-j),现需移动人的位置使每一个原先坐着人的座位空置,求最少代价。
思路:
注意到,当人员移动的所有目标位置确定后,代价最小的移动方案必然是由小到大对应相关位置的移动方案。
换言之,设数列{an}为n个原位置,{bn}为n个目标位置,有最小值Sn,其中Sn是数组{cn}={abs(an-bn)}的各项和,其证明可以从绝对值相加减的大小判断中得到。
那么可知:我们只需找出满足代价总和最小的对应个位置即可。
设原先情况下坐着人的座位有a个,不坐人的座位有b个,将前二者的位置信息分别存放至两个大小为a和b的数组中。
令DP第一维为空座位的位置,第二维为已分配座位的数目,可得以下状态转移方程:
1. dp[i][0]=0 (分配0个座位的花费当然是0)
2. dp[i][j]=min(dq[i-1][j],dq[i-1][j-1]+abs(p[i]-s[j])) (分配至第i位置时,可以分配位置并加上当前的位置花费,也可以不分配)
3. 其他的都设置成inf
代码如下:
#include<iostream>
#define ll long long
using namespace std;
ll s[5001],p[5001],dp[5001][5001];
int main(){
ll n,i,j,k,zc,sl1=0,sl2=0;
cin>>n;
for(i=1;i<=n;i++){
cin>>zc;
if(zc==1) s[++sl1]=i;
else p[++sl2]=i;
}
for(i=0;i<=sl2;i++){
for(j=1;j<=sl1;j++){
dp[i][j]=1e12;
}
}
for(i=1;i<=sl2;i++) dp[i][0]=0;
for(i=1;i<=sl2;i++){
for(j=1;j<=sl1;j++){
dp[i][j]=min(dp[i][j],dp[i-1][j-1]+abs(p[i]-s[j]));
dp[i][j]=min(dp[i][j],dp[i-1][j]);
}
}
cout<<dp[sl2][sl1];
return 0;
}