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的下标都拿出来,放进数组pos1\pos0里面,问题就变成了拿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;
}
上一篇:递归3之汉诺塔的实现


下一篇:力扣-349题(Java)-双指针