6848. 【2020.11.03提高组模拟】融入社会的计划

64 篇文章 0 订阅
14 篇文章 0 订阅

Description

Input

Output

一行一个整数表示答案。 

upd:  无解输出-1。 
 

Sample Input

Sample Input1
6 6 13
11 1 1 4 5 5 

Sample Input2
11 11 29
10 9 4 11 7 3 3 3 4 5 4 

 

Sample Output

Sample Output1
4
11 1 5 4 5 5 

Sample Output2
11
10 9 4 11 7 7 4 7 4 7 4

 

Data Constraint

Solution

解法一:

贪心。

对于能需要提高到 L 的就提高。

对于当前不提高都已经大于 R 的,考虑减小前面的某些数,使当前状态合法,即  比R大了多少 就减去多少(记为val)。

于是记录每个数的变化量(增加量),从i-1 往前 递归,i-1减去val ,i-2再加上val....以此类推,若不能减(当减当某个位置增加量为负数)就无解,能减就减。

时间复杂度   O(理论复杂度是 n^2 但实际效果为 n)。

法二:
dp。

最朴素做法:设f[ i ][ j ]表示第i个位置选了 j,转移枚举当前填x的最小花费。

f[i+1][x]=min(f[i][j]+x-a[i+1])~~~~~~~~~(x\geq a[i+1],~x+a[i]\epsilon [L,R])

记录转移过来的位置,倒退回去即可。

优化后时间O(nL)

发现f[ i ]一定是   前面一段是不合法,后面一段不合法,中间一段答案递增 。

那么最小的f一定是合法区间的左端点。

那么先从前往后推出最后一个f[ n ]的合法区间的左端点,令a[ n ]等于它,再往前推,依次选取左端点即可。

时间复杂度O(n)

Code1 

#include<cstdio>
#define I int
#define ll long long
#define F(i,a,b)  for(I i=a;i<=b;i++)
#define N 50004
using namespace std;
I l,r,n,k,a[N],b[N],p,now;
ll ans;
void R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I dg(I x,I v){
	b[x]-=v;
	if(b[x]<0) return 0;
	if(x==1) return 1;
	b[x-1]+=v;
	if(a[x-2]+b[x-2]+a[x-1]+b[x-1]>r) return dg(x-2,a[x-2]+b[x-2]+a[x-1]+b[x-1]-r);
	return 1;
}
I main(){
	freopen("plan.in","r",stdin);
	freopen("plan.out","w",stdout);
	R(n),R(l),R(r);
	F(i,1,n) R(a[i]);
	F(i,2,n){
		if(a[i]+a[i-1]+b[i-1]<l) b[i]=l-a[i]-a[i-1]-b[i-1];
		if(a[i]+a[i-1]+b[i-1]>r&&!dg(i-1,a[i]+a[i-1]+b[i-1]-r)){ans=-1;break;}
	}
	if(ans!=-1){F(i,1,n) ans+=b[i];}
	printf("%lld\n",ans);
	if(ans!=-1){F(i,1,n) printf("%d ",a[i]+b[i]);}
	return 0;
}

Code2(dp)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define ll long long
#define F(i,a,b)  for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 5004
using namespace std;
I l,r,n,k,a[N],g[N][N],p;
ll ans,c[N],f[N][N],inf;
void R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I main(){
	freopen("plan.in","r",stdin);
	freopen("ans.out","w",stdout);
	R(n),R(l),R(r);
	F(i,1,n) R(a[i]);
	mem(f,127);
	ans=f[1][1];
	F(i,a[1],r) f[1][i]=i-a[1];
	F(i,1,n-1){
		F(j,a[i],r) if(f[i][j]!=ans){
			p=a[i+1]>l?r-j:l;
			F(k,max(a[i+1],l-j),p){
				if(f[i][j]+k-a[i+1]<f[i+1][k]) f[i+1][k]=f[i][g[i+1][k]=j]+k-a[i+1];
			}
		}
	}
	F(i,a[n],r) if(f[n][i]<ans) ans=f[n][k=i];
	if(ans==f[0][0]){printf("-1\n");return 0;}
	printf("%lld\n",ans);
	Fd(i,n,1){
		a[i]=k;
		k=g[i][k];
	}
	F(i,1,n) printf("%d ",a[i]);
	return 0;
}

Code3

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define I int
#define ll long long
#define F(i,a,b)  for(I i=a;i<=b;i++)
#define Fd(i,a,b) for(I i=a;i>=b;i--)
#define mem(a,b) memset(a,b,sizeof a)
#define N 50004
using namespace std;
I l[N],r[N],n,k,a[N],p,now;
ll ans;
void R(I &x){
	x=0;I w=1;char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') w=-1;c=getchar();}
	while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
	x*=w;
}
I main(){
	freopen("plan.in","r",stdin);
	freopen("plan.out","w",stdout);
	R(n),R(l[0]),R(r[0]);
	F(i,1,n) R(a[i]);
	l[1]=a[1],r[1]=r[0];
	F(i,2,n){
		l[i]=max(a[i],l[0]-r[i-1]);
		r[i]=r[0]-l[i-1];
	}
	Fd(i,n,1){
		l[i-1]=max(l[i-1],l[0]-l[i]);
		r[i-1]=min(r[i-1],r[0]-l[i]);
		if(l[i]>r[i]){
			printf("-1\n");return 0;
		}
		ans+=l[i]-a[i];
	}
	printf("%lld\n",ans);
	F(i,1,n) printf("%d ",l[i]);
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值