【NOIP模拟】序列

来源:互联网 发布:淘宝天猫人工投诉电话 编辑:程序博客网 时间:2024/05/17 19:58

Description

这里写图片描述

Solution

这道题有两个方法。

方法1:差分加贪心

首先可以求出每个点从a[i]到b[i]的步数c[i]。
然后处理出两两之间的差分d[i]。显然在不调整之前(加4),答案的值是max(0,d[i]),画一下图就知道了。
那么假设现在对区间[l,r]的每个数加4,那么对于差分的影响,只会影响到l和l-1的差分还有r和r+1之间的差分。
如果现在这里差分有一个3(或2),那么需要在前面找一个-3或-2,因为这样加4之后可以让max(差分,0)更小,而且因为中间的高度已经被两边抵消了,所以加4不会产生新的贡献。
然后O(n)就过去了

Code

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=100007;int i,j,k,l,t,n,m,ans,cas,r,da,da1,ans1,ans2[maxn];int a[maxn],b[maxn],c[maxn],d[maxn],yi,er;int f[maxn][11],g[maxn][11];int main(){    for(scanf("%d",&cas);cas;cas--){        scanf("%d",&n);        fo(i,1,n)scanf("%d",&a[i]);        fo(i,1,n)scanf("%d",&k),c[i]=(k-a[i]+4)%4;        fo(i,1,n)d[i]=c[i]-c[i-1];        yi=er=ans=0;        fo(i,1,n)ans+=max(0,d[i]);        fo(i,1,n){            switch(d[i]){                case -3:yi++;break;                case -2:er++;break;                case 1:break;                case 0:break;                case -1:break;                case 3:{                    if(yi)yi--,ans-=2;                    else if(er)ans--,er--;                    break;                }                case 2:{                    if(yi)ans--,yi--,er++;                    break;                }            }        }        printf("%d\n",ans);    }}

方法2:带证明的优美的DP

设f[i][j]表示i这个位加j次4的最小值。
转移很显然,是一个水DP。
但是j的值域怎么办?
事实上,j开到5就好了,可以用上面的差分证明,因为有5中情况,叠在一起,同一个位置最多加5次4。

Code

#include<iostream>#include<cstdio>#include<cstring>#include<cmath>#include<algorithm>#define fo(i,a,b) for(i=a;i<=b;i++)using namespace std;const int maxn=100007;int i,j,k,l,t,n,m,ans,cas,r,da,da1;int a[maxn],b[maxn],c[maxn];int f[maxn][6];bool bz,az;int chu(int x,int y){   if(x<=y)return y-x;   else return (4-x)+y;}int main(){//  freopen("fan.in","r",stdin);    for(scanf("%d",&cas);cas;cas--){        scanf("%d",&n);        fo(i,1,n)scanf("%d",&a[i]);        da=0;        fo(i,1,n)scanf("%d",&b[i]),c[i]=chu(a[i],b[i]);        memset(f,127,sizeof(f));        f[0][0]=0;        fo(i,1,n){            fo(j,0,5){                fo(k,0,5){                    if(c[i]+j*4<=c[i-1]+k*4)f[i][j]=min(f[i][j],f[i-1][k]);                    else f[i][j]=min(f[i][j],f[i-1][k]+abs((c[i]+j*4)-(c[i-1]+k*4)));                }            }        }        ans=0x7fffffff;        fo(i,0,5)ans=min(f[n][i],ans);        printf("%d\n",ans);    }}
2 0
原创粉丝点击