您的位置:首页 > 其它

【NOIP模拟】序列

2016-09-19 20:38 309 查看

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
[i],ans);
printf("%d\n",ans);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: