[Atcoder Yahoo Contest 2019]D.Ears(动态规划)

Score:600600600 points

题面

传送门
翻译有时间再补…

题解

体验感极差,考试的时候手残把1打成了2Debug了半个小时,害的F题都没做。
先将题目转换一下:给你一条链,顺次连接着n+1n+1n+1个点,每条边有一个边权AiA_iAi,在有一个人从任意一个起点PPP出发,设他经过第iii条边的次数DiD_iDi,求∑i=1n∣Ai+Di∣\sum^{n}_{i=1}|A_i+D_i|i=1nAi+Di的最小值。
首先需要证明一个结论:
A_i为奇数,等价于Ai=1A_i=1Ai=1
A_i为大于0的偶数,等价于Ai=2A_i=2Ai=2
证明很简单,可以发现走到边的同一侧的DiD_iDi奇偶性相同,由于你经过这条边的时候可以任意来回跳动偶数次,那么答案一定不会变的更劣。
考虑一个人从PPP点出发,有两种方案:
1.先往左边走,然后回到PPP点,再往右边走,不再回来。
2.先往右边走,然后回到PPP点,再往左边走,不再回来。
这很像一个dp。
定义状态L1i,L2i,R1i,R2iL1_i,L2_i,R1_i,R2_iL1i,L2i,R1i,R2i
分别代表从iii点向左走需要回来的最小代价,从iii点向左走不需要回来的最小代价,从iii点向右走需要回来的最小代价,从iii点向右走不需要回来的最小代价。

L1[i]={min⁡(L1i−1+2,lsumi)(Ai==0)min⁡(L1i−1+1,lsumi)(Ai==1)min⁡(L1i−1,lsumi)(Ai==2) L1[i]=\begin{cases} \min(L1_{i-1}+2,lsum_i)(A_i==0)\\ \min(L1_{i-1}+1,lsum_i)(A_i==1)\\ \min(L1_{i-1},lsum_i)(A_i==2)\\ \end{cases} L1[i]=min(L1i1+2,lsumi)(Ai==0)min(L1i1+1,lsumi)(Ai==1)min(L1i1,lsumi)(Ai==2)
L2[i]={min⁡(L1i,min(L2i−1+1,lsumi))(Ai==0)min⁡(L1i,min(L2i−1,lsumi))(Ai==1)min⁡(L1i,min(L2i−1+1,lsumi))(Ai==2) L2[i]=\begin{cases}\min(L1_i,min(L2_{i-1}+1,lsum_i))(A_i==0)\\ \min(L1_i,min(L2_{i-1},lsum_i))(A_i==1)\\ \min(L1_i,min(L2_{i-1}+1,lsum_i))(A_i==2)\\ \end{cases} L2[i]=min(L1i,min(L2i1+1,lsumi))(Ai==0)min(L1i,min(L2i1,lsumi))(Ai==1)min(L1i,min(L2i1+1,lsumi))(Ai==2)
R同理。
需要注意的是在走路的过程中可以随时不走后面的路调头,代价是前面/后面iii个数的AiA_iAi之和。
答案就是min(L1[i]+R2[i],L2[i]+R1[i])min(L1[i]+R2[i],L2[i]+R1[i])min(L1[i]+R2[i],L2[i]+R1[i])
时间复杂度:O(n)O(n)O(n)
[题解好像还有一种神仙写法,我菜爆了…]

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
#define MAXN 200000
#define LL long long
#define DB double
#define FR first
#define SE second
int a[MAXN+5];
LL ans;
LL L1[MAXN],L2[MAXN+5],R1[MAXN+5],R2[MAXN+5];
LL lsum[MAXN+5],rsum[MAXN+5];
int n;
int get_num(int p)
{
	if(p==0)return 0;
	if(p%2==1)return 1;
	return 2;
}
int main()
{
	ans=1000000000;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    for(int i=1;i<=n;i++)
    lsum[i]=lsum[i-1]+a[i];
    for(int i=n-1;i>=0;i--)
    rsum[i]=rsum[i+1]+a[i+1];
    for(int i=1;i<=n;i++)a[i]=get_num(a[i]);
    for(int i=1;i<=n;i++)
    {
        if(a[i]==0)L1[i]=min(L1[i-1]+2,lsum[i]);
        else if(a[i]==1)L1[i]=min(L1[i-1]+1,lsum[i]);
        else L1[i]=min(L1[i-1],lsum[i]);
        if(a[i]==0)L2[i]=min(L1[i],min(L2[i-1]+1,lsum[i]));
        else if(a[i]==1)L2[i]=min(L1[i],min(L2[i-1],lsum[i]));
        else L2[i]=min(L1[i],min(L2[i-1]+1,lsum[i]));
    }
    for(int i=n-1;i>=0;i--)
    {
        if(a[i+1]==0)R1[i]=min(R1[i+1]+2,rsum[i]);
        else if(a[i+1]==1)R1[i]=min(R1[i+1]+1,rsum[i]);
        else R1[i]=min(R1[i+1],rsum[i]);
        if(a[i+1]==0)R2[i]=min(R1[i],min(R2[i+1]+1,rsum[i]));
        else if(a[i+1]==1)R2[i]=min(R1[i],min(R2[i+1],rsum[i]));
        else R2[i]=min(R1[i],min(R2[i+1]+1,rsum[i]));
    }
    for(int i=0;i<=n;i++)
    {
    	//printf("%d %lld %lld\n",i,L1[i]+R2[i],L2[i]+R1[i]);
        ans=min(ans,min(L1[i]+R2[i],L2[i]+R1[i]));
    }
    printf("%lld\n",ans);
}

转载于:https://www.cnblogs.com/Panda-hu/p/11145746.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值