E: The Contest //线性dp

题目链接

http://codeforces.com/contest/1257/problem/E

题意

初始有三个组,共n个数,现在要求改变一些数的位置,使得第一个组为这n个数的前缀,第三个组为这n个数的后缀,第二个组为其余数字,移动后一些组可能为空,要求最少移动次数。

思路

按照顺序,前一个数字所放的组的编号会影响到后续数字放的组的编号,比如如果数字 1 1 1放第三组,那么后续 n − 1 n-1 n1个数组也就只能放第三组,但如果数字 1 1 1放第二组,那么数字二就有两个选择:放第二组或放第三组。依次来看,后一个数字的位置状态受到前一个数字位置状态的影响,所以可以通过动态规划来枚举每个数字的位置状态,来求出这 n n n个数字的最小移动次数。
假设数字 i i i原来所在的组数为 a [ i ] a[i] a[i],也就是输入时的位置。
我们定义: d p [ i ] [ j ] dp[i][j] dp[i][j]为轮到了数字 i i i选组,数字 i i i选第 j j j组后的最小移动次数,那么我们可以得到状态转移方程:
(1) d p [ i ] [ 1 ] = d p [ i − 1 ] [ 1 ] + ( a [ i ] ! = 1 ) dp[i][1]=dp[i-1][1]+(a[i]!=1) dp[i][1]=dp[i1][1]+(a[i]!=1)
(2) d p [ i ] [ 2 ] = m i n ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] ) + ( a [ i ] ! = 2 ) dp[i][2]=min(dp[i-1][1],dp[i-1][2])+(a[i]!=2) dp[i][2]=min(dp[i1][1],dp[i1][2])+(a[i]!=2)
(3) d p [ i ] [ 3 ] = m i n ( d p [ i − 1 ] [ 1 ] , d p [ i − 1 ] [ 2 ] , d p [ i − 1 ] [ 3 ] ) + ( a [ i ] ! = 3 ) dp[i][3]=min(dp[i-1][1],dp[i-1][2],dp[i-1][3])+(a[i]!=3) dp[i][3]=min(dp[i1][1],dp[i1][2],dp[i1][3])+(a[i]!=3)//可能第二组为空,所以前一个数在第一组
所以 n n n个数的最小移动次数即为 d p [ n ] [ 1 ] , d p [ n ] [ 2 ] , d p [ n ] [ 3 ] dp[n][1],dp[n][2],dp[n][3] dp[n][1],dp[n][2],dp[n][3]中的较小者。

参考代码
#include <bits/stdc++.h>

using namespace std;
const int maxn=2e5+7;
typedef long long ll;
const int inf=0x3f3f3f3f;
int k1,k2,k3,n,x;
int a[maxn],dp[maxn][5];
int main()
{
    scanf("%d%d%d",&k1,&k2,&k3);
    n=k1+k2+k3;
    for(int i=1;i<=n;i++){
        dp[i][1]=dp[i][2]=dp[i][3]=inf;//初始化dp数组为无穷大
        scanf("%d",&x);
        if(i<=k1)a[x]=1;//数字x原来所在的组数为a[x]
        else if(i<=k1+k2)a[x]=2;
        else a[x]=3;
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=3;j++){//枚举前一个数字所在的组数
            for(int k=j;k<=3;k++){//枚举后一个数字所在的组数
                dp[i][k]=min(dp[i][k],dp[i-1][j]+(a[i]!=k));
            }
        }
    }
    cout<<min(dp[n][1],min(dp[n][2],dp[n][3]))<<'\n';
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值