zoukankan      html  css  js  c++  java
  • THUSC2016 成绩单 和 LOJ3226 Greedy Pie Eaters

    成绩单

    期末考试结束了,班主任 L 老师要将成绩单分发到每位同学手中。L老师共有 (n) 份成绩单,按照编号从 (1)(n) 的顺序叠放在桌子上,其中编号为 (i) 的成绩单分数为 (W_i)

    成绩单是按照批次发放的。发放成绩单时,L 老师会从当前的一叠成绩单中抽取连续的一段,让这些同学来领取自己的成绩单。当这批同学领取完毕后,L 老师再从剩余的成绩单中抽取连续的一段,供下一批同学领取。经过若干批次的领取后,成绩单将被全部发放到同学手中。

    然而,分发成绩单是一件令人头痛的事情,一方面要照顾同学们的心理情绪,不能让分数相差太远的同学在同一批领取成绩单;另一方面要考虑时间成本,尽量减少领取成绩单的批次数。对于一个分发成绩单的方案,我们定义其代价为:

    [a imes k+b imes sum_{i=1}^{k}( ext{max}_i- ext{min}_i)^2 ]

    其中 (k) 是分发的批次数,对于第 (i) 批分发的成绩单,( ext{max}_i) 是最高分数,( ext{min}_i) 是最低分数,(a)(b)是给定的评估参数。
    现在,请你帮助 L 老师找到代价最小的分发成绩单的方案,并将这个最小的代价告诉 L 老师。当然,分发成绩单的批次数 (k) 是由你决定的。

    (n leq 50, a leq 1500, b leq 10, w_i leq 1000)

    题解

    https://blog.csdn.net/qq_39677783/article/details/86898654

    考虑到这题的抽取方式:每次从中间抽取一段,然后两边的又会拼起来,所以区别于序列划分类问题,前缀DP的状态不够清晰无法转移,可以想到区间DP。

    先设(g(i,j))表示将([i,j])这段区间的数全部消掉的最小代价,那么最终答案就是(g(1,n))。但是这样会有问题:我们需要知道每次消去的(max_i)(min_i)是多少,才能方便转移。那么我们可以这样做:首先对(w_i)离散化,假设(tmp_i)表示原数组(w)中第(i)小的数;对于一个(g(i,j)),我们可以枚举值域区间([l,r]),表示将([i,j])这段区间里的数全部消掉之前最后一次消去的数都在值域范围([l,r])当中。那么最后一次消去显然会产生(a+b imes(tmp_r-tmp_l)^2)的代价。那么接下来的问题就是:将区间([i,j])中的数消到只剩下值域范围在([l,r])中的数,最小代价是多少,不妨设其为(f(i,j,l,r))

    这样一来我们(g(i,j))的转移方程就有了:

    [g(i,j)=min_{lleq r}{f(i,j,l,r)+a+b imes(tmp_r-tmp_l)^2} ]

    下面考虑(f(i,j,l,r))的转移。一个显然的思路是

    [f(i,j,l,r)=min_{ileq k< j}{f(i,k,l,r)+f(k+1,j,l,r)} ]

    然而很不幸这样做会漏掉一些情况。

    正确的做法是:首先我们可以找到([i,j])区间里左边第一个不在值域范围([l,r])中的数的位置(p),以及右边第一个不在值域范围([l,r])中的数的位置(q),如果存在这样的区间([p,q])(如果不存在当然就不用管了),那么

    [f(i,j,l,r)=min(g(p,q),minlimits_{ileq k< j}{f(i,k,l,r)+f(k+1,j,l,r)}) ]

    好了,这两个转移方程出来之后,剩下的就是区间DP套路了。当然是枚举区间长度再枚举左端点做DP就行了。

    时间复杂度(O(n^5))

    CO int N=60,inf=1e9;
    int w[N],tmp[N];
    int f[N][N][N][N],g[N][N];
    
    int main(){
    	int n=read<int>(),a=read<int>(),b=read<int>();
    	for(int i=1;i<=n;++i) read(w[i]);
    	copy(w+1,w+n+1,tmp+1);
    	sort(tmp+1,tmp+n+1);
    	int all=unique(tmp+1,tmp+n+1)-tmp-1;
    	for(int i=1;i<=n;++i)
    		w[i]=lower_bound(tmp+1,tmp+all+1,w[i])-tmp;
    	for(int i=1;i<=n;++i){
    		g[i][i]=a;
    		for(int l=1;l<=all;++l)for(int r=l;r<=all;++r)
    			if(w[i]<l or w[i]>r) f[i][i][l][r]=a;
    	}
    	for(int len=2;len<=n;++len)for(int i=1,j=i+len-1;j<=n;++i,++j){
    		g[i][j]=inf;
    		for(int l=1;l<=all;++l)for(int r=l;r<=all;++r){
    			int p=0,q=0;
    			for(int t=i;t<=j;++t)
    				if(w[t]<l or w[t]>r) {p=t; break;}
    			for(int t=j;t>=i;--t)
    				if(w[t]<l or w[t]>r) {q=t; break;}
    			if(p and q) f[i][j][l][r]=g[p][q];
    			else f[i][j][l][r]=inf;
    			for(int k=i;k<j;++k)
    				f[i][j][l][r]=min(f[i][j][l][r],f[i][k][l][r]+f[k+1][j][l][r]);
    			g[i][j]=min(g[i][j],f[i][j][l][r]+a+b*(tmp[r]-tmp[l])*(tmp[r]-tmp[l]));
    		}
    	}
    	printf("%d
    ",g[1][n]);
    	return 0;
    }
    

    Greedy Pie Eaters

    Farmer John 有 (M) 头奶牛,为了方便,编号为 (1ldots M)。这些奶牛平时都吃青草,但是喜欢偶尔换换口味。Farmer John 一天烤了 (N) 个派请奶牛吃,这 (N) 个派编号为 (1ldots N)。第 (i) 头奶牛喜欢吃编号在 ([l_i,r_i]) 中的派(包括两端),并且没有两头奶牛喜欢吃相同范围的派。第 (i) 头奶牛有一个体重 (w_i),这是一个在 ([1, 10^6]) 中的正整数。

    Farmer John 可以选择一个奶牛序列 (c_1,c_2,ldots c_K),并让这些奶牛按这个顺序轮流吃派。不幸的是,这些奶牛不知道分享!当奶牛 (c_i) 吃派时,她会把她喜欢吃的派都吃掉——也就是说,她会吃掉编号在 ([l_{c_i},r_{c_i}]) 中所有剩余的派。Farmer John 想要避免当轮到一头奶牛吃派时,她所有喜欢的派在之前都被吃掉了这样尴尬的情况。因此,他想让你计算,要使奶牛按 (c_1,c_2,ldots c_K) 的顺序吃派,轮到这头奶牛时她喜欢的派至少剩余一个的情况下,这些奶牛的最大可能体重((w_{c_1}+w_{c_2}+ldots +w_{c_K}))是多少。

    对于全部数据,(1le Nle 300,1le Mle frac{N(N-1)}{2},1le l_ile r_ile N,1le w_ile 10^6)

    题解

    http://jklover.hs-blog.cf/2020/06/07/Loj-3226-Greedy-Pie-Eaters/#more

    首先可以假定所有(inom{n}{2})个区间都对应了一头牛,没有给出的可以将其牛的重量看作 0 .

    当某头牛要吃区间 ([l,r]​) 时,若剩下的派大于 (1​) ,可以先让其他牛吃到还剩一个,于是每头牛都恰好吃掉一个派.

    (f(l,r)​) 表示把 (l,r​) 内的派吃完能获得的最大收益,转移时枚举最后被吃的派是 (k​) ,

    [f(l,r)=max_{lle kle r} f(l,k-1)+f(k+1,r)+g(l,r,k) ]

    其中 (g(l,r,k)=max_{lle xle kle y le r} A_{x,y}) ,表示用区间不超出 ([l,r]) 的牛吃掉第 (k) 个派能获得的最大收益.

    转移时考虑 ([l,r]) 这个区间的贡献就行了.

    [g(l,r,k)=max lbrace A_{l,r},g(l+1,r,k),g(l,r-1,k) brace ]

    时间复杂度 (O(n^3)) .

    CO int N=310;
    int a[N][N],g[N][N][N],f[N][N];
    
    int main(){
    	int n=read<int>();
    	for(int m=read<int>();m--;){
    		int w=read<int>(),l=read<int>(),r=read<int>();
    		a[l][r]=w;
    	}
    	for(int len=1;len<=n;++len)
    		for(int l=1,r=l+len-1;r<=n;++l,++r)
    			for(int k=l;k<=r;++k){
    				g[l][r][k]=max(a[l][r],max(g[l+1][r][k],g[l][r-1][k]));
    				f[l][r]=max(f[l][r],f[l][k-1]+f[k+1][r]+g[l][r][k]);
    			}
    	printf("%d
    ",f[1][n]);
    	return 0;
    }
    
  • 相关阅读:
    光棒效果的几种方法
    jQuery中的事件与动画
    jQuery中.bind() .live() .delegate() .on()的区别
    JavaScript基础
    jQuery选择器课堂随笔
    Leetcode_34【在排序数组中查找元素的第一个和最后一个位置】
    Leetcode_33【搜索旋转排序数组】
    Leetcode_32【最长有效括号】
    Leetcode_31【下一个排列】
    Leetcode_30【串联所有单词的子串】
  • 原文地址:https://www.cnblogs.com/autoint/p/13055033.html
Copyright © 2011-2022 走看看