Educational Codeforces Round 109 D. Armchairs (dp)
题意:有一个长度为n的0/1数列,1的个数小于等于0的个数,可以把i位置上的1移到j位置(如果j位置是0),i位置变成0,j位置变成1,代价是|i-j|,问使得所有原先是1的位置都变成0的代价最小是多少。 (3<=n<=5000)
分析:我们可以把1/0的下标都拿出来,放进数组pos1pos0里面,问题就变成了拿0去匹配1,求最小匹配代价。
-
dp[i][j]表示前i个1与前j个0完成了匹配(那么第i个1肯定是匹配了第j个0的)的最小代价 ,不合法的状态(比如前4个1与前3个0完成匹配)设为INF 初始化dp为INF,dp[0][i]=0 转移:dp[i][j]=min(dp[i-1][k])+ |pos1[i]-pos1[j]| k∈[0,j-1] 这样的话是O(n³),但是我们可以开一个数组记录前缀最小值,优化成O(n²)
#include<bits/stdc++.h> using namespace std; #define ll long long #define pii pair<int,int> const int maxn = 5e3+10; const int mx = 40; const int mod = 1e9+7; const ll inf = 0x3f3f3f3f3f3f3f3f; const int INF = 1e9+7; int n; int a[maxn]; ll dp[maxn][maxn];//前i个1 匹配前j个0 i和j匹配 代价 ll mmin[maxn][maxn]; vector<int> pos0,pos1; int main() { scanf("%d",&n); pos0.push_back(0),pos1.push_back(0); for(int i=1;i<=n;i++) { scanf("%d",a+i); if(a[i]) pos1.push_back(i); else pos0.push_back(i); } if(pos1.empty()) { puts("0"); return 0; } ll ans=INF; int n0=pos0.size()-1,n1=pos1.size()-1; memset(mmin,0x3f3f3f,sizeof mmin); memset(dp,0x3f3f3f,sizeof dp); for(int i=0;i<=n0;i++) dp[0][i]=mmin[0][i]=0; for(int i=1;i<=n1;i++) { for(int j=1;j<=n0;j++) { dp[i][j]=mmin[i-1][j-1]+abs(pos1[i]-pos0[j]); } for(int j=1;j<=n0;j++) { mmin[i][j]=min(dp[i][j],mmin[i][j-1]); } } for(int i=1;i<=n0;i++) { ans=min(ans,dp[n1][i]); } cout<<ans; return 0; }