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


    (Problem)

    给你(n)和长度为(n)的数组(a[])

    可以使(a[i])增加成(a[i]'),其代价为(a[i]'-a[i])

    求花费最小的代价使得满足对于任意的(i in [2,n])(L<=a[i-1]+a[i]<=R)(L,R)为给定的数。

    (n<=10^5.L,R<=10^6)

    (force)

    首先有一个显然的暴力(DP)

    (f[i][x])表示(a[i])变成(x)前面都满足条件的最小代价。

    (f[i][x])可以转移到(f[i+1][k])(kin [L-x,R-x])

    然后可以发现,当(a[i]>=L)的时候,再增加其实没有什么意义的。

    所以枚举的界限缩小,时间复杂度为(O(n*L^2))(35)分到手。

    (Solution 1)

    考虑可撤销贪心,顺序改变值,只考虑(i)以前的都符合条件。

    (pl[i])表示(a[i])要加的数。

    对于当前位置(i)

    (sum=a[i - 1] + a[i] + pl[i - 1])

    (L<=sum<=R),则可以(a[i])不需要改变(即(pl[i])不需要变)

    (sum<L),则(pl[i]=L-sum)即可。

    (sum>R),则通过一个(check(i-1))函数来处理一下。

    (check(x))函数:

    对于新的(sum=a[i]+a[i+1]+pl[i]+pl[i+1])

    若当前的(L<=sum<=R),则返回可行((1)

    若当前的(sum>R),则(pl[i])需要减小,并判断是否小于(0)

    若当前的(sum<L),则(pl[i])需要增加,并判断(check(x-1))

    如此即可。(看上去似乎可能会(TLE),但是跑得飞快

    (Code)

    #include <cstdio>
    #define N 50010
    #define db double
    #define ll long long
    #define mem(x, a) memset(x, a, sizeof x)
    #define mpy(x, y) memcpy(x, y, sizeof y)
    #define fo(x, a, b) for (int x = (a); x <= (b); x++)
    #define fd(x, a, b) for (int x = (a); x >= (b); x--)
    #define go(x) for (int p = tail[x], v; p; p = e[p].fr)
    using namespace std;
    int n, L, R, a[N], pl[N];
    ll all = 0;
    
    inline int read() {
    	int x = 0, f = 0; char c = getchar();
    	while (c < '0' || c > '9') f = (c == '-') ? 1 : f, c = getchar();
    	while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    	return f ? -x : x;
    }
    
    bool check(int x, int sum) {
    	if (x == 0 || (sum >= L && sum <= R)) return 1;
    	if (sum > R) {
    		pl[x] -= sum - R;
    		if (pl[x] < 0) return 0;
    		return check(x - 1, a[x - 1] + a[x] + pl[x - 1] + pl[x]);
    	}
    	pl[x] += L - sum;
    	return check(x - 1, a[x - 1] + a[x] + pl[x - 1] + pl[x]);
    }
    
    int main()
    {
    	freopen("plan.in", "r", stdin);
    	freopen("plan.out", "w", stdout);
    	n = read(), L = read(), R = read();
    	fo(i, 1, n) a[i] = read();
    	fo(i, 2, n) {
    		int sum = a[i - 1] + a[i] + pl[i - 1];
    		if (sum < L) {pl[i] = L - sum; continue;}
    		if (sum > R && ! check(i - 1, sum))
    			return 0 & printf("-1
    ");
    	}
    	fo(i, 1, n) all += pl[i]; printf("%lld
    ", all);
    	fo(i, 1, n) printf("%d ", a[i] += pl[i]);
    	/*
    	fo(i, 2, n)
    		if (a[i - 1] + a[i] < L || a[i - 1] + a[i] > R)
    			printf("wrong
    ");
    	*/
    	return 0;
    }
    

    (Solution 2)

    考虑原先的(DP)

    性质(1):发现(f[i,x])转移到(f[i+1,j])为一个区间,可以单调队列优化。

    性质(2):且又发现转移到的([l,r])(f[i+1][j])的值是单调不减的。

    (le[i],ri[i])表示对于(f[i])的可行的区间。

    则答案就是(f[n][le[n]]),而我们又可以从中倒推出是从([L-le[n],R-le[n]])([le[n-1],ri[n-1]])的交转移过来的。

    而由于(f[x])的单调性,所以一定是交的最左端点最后,这样线性地倒推回去就可以求解了。

    (Code by ZFY)

    #include<cstdio>
    #include<cstring>
    using namespace std;
    int fl[51000],fr[51000],l[51000],r[51000],a[51000],c[51000];
    inline int mymax(int x,int y){return x>y?x:y;}
    int main()
    {
    	freopen("plan.in","r",stdin);
    	freopen("plan.out","w",stdout);
    	int n,L,R;scanf("%d%d%d",&n,&L,&R);
    	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    	for(int i=1;i<n;i++)l[i]=L-a[i]-a[i+1],r[i]=R-a[i]-a[i+1];
    	fl[1]=0;fr[1]=0x3f3f3f3f;
    	for(int i=2;i<=n;i++)
    	{
    		fl[i]=mymax(l[i-1]-fr[i-1],0);
    		fr[i]=r[i-1]-fl[i-1];
    	}
    	if(fl[n]>fr[n]){puts("-1");return 0;}
    	long long ss=fl[n];c[n]=fl[n];
    	for(int j=n-1;j>=1;j--)
    	{
    		c[j]=mymax(l[j]-c[j+1],fl[j]);
    		ss=ss+c[j];
    	}
    	printf("%lld
    ",ss);
    	for(int i=1;i<n;i++)printf("%d ",a[i]+c[i]);
    	printf("%d
    ",a[n]+c[n]);
    	return 0;
    }
    
  • 相关阅读:
    python中filter(),map()和reduce()的用法及区别
    Python中的单例模式的几种实现方式的及优化
    python标准库和第三方库的区别
    django和flask的区别
    wtforms
    protobuf学习
    人物FSM
    策略模式
    虚函数调用机制
    虚析构函数
  • 原文地址:https://www.cnblogs.com/jz929/p/13923000.html
Copyright © 2020-2023  润新知