模拟、枚举与贪心4

贪心最重要的是确定策略,并能够证明此为实际上的最优策略。

给定长度为n的整数数列{Ai},找出两个整数ai和aj(i

最简单的方法:暴力循环。显然可以再优化。

不妨先用一层循环,维护到坐标i为止的最小值,后再循环用每个坐标上的值减去当前位置遍历到的最小值(即为维护出来的值),取其中的最大值。

#include
#include 
#include
#include
using namespace std;
typedef long long ll;
const ll maxx = 0x7f7f7f7f;
int n,minn[100],a[100],ans=-maxx;
int main(){
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>a[i];
	minn[n]=a[n];
	for(int i=n-1;i>=1;--i)
		minn[i]=min(minn[i+1],a[i]);
	for(int i=1;i<=n;++i){
		ans=max(ans,a[i]-minn[i]);
	}
	cout<

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

[NOIP1998]拼数

设有n个正整数(n ≤ 20),将它们联接成一排,组成一个最大的多位整数。
例如:n=3时,3个整数13,312,343联接成的最大整数为:34331213
又如:n=4时,4个整数7,13,4,246联接成的最大整数为:7424613

容易想到,当长度相同时,只需一位位比较相同位置,最高位较大的直接排到前面。但若是不同长度,就要仔细考虑,如233和2331,当比较到第二个串中的1时,需要与第一个串从头开始比较(即比较2),需要暴力循环。更优的策略就是将两个串并在一起,再比较2332331和2331233。证明此为最优策略的过程如下:

假设有两个串a、b,且a>b,则可以证明当有任意串c在a、b之间时,恒有acb>bca,且由冒泡排序的思想得,当任意两串满足上述条件时,整体也必然是满足条件的。

#include
#include
#include
#include
using namespace std;
int n;
string s[100];
bool cmp(string a,string b){
	return a+b>b+a;
}
int main(){
	cin>>n;
	for(int i=1;i<=n;++i)
		cin>>s[i];
	sort(s+1,s+1+n,cmp);
	for(int i=1;i<=n;++i)
		cout<

Given Length and Sum of Digits

求长度为m,每一位数字和为S的最大数和最小数。m<=100,S<=900

思路并不难,可以容易的想到,最大数就是尽量往高位填最大值,最后在末尾填剩余值或0;最小值就是尽量往低位填最大值,但要保证最高位不为0,至少为1 。

题目的关键是怎样写起来让代码简单。思考发现,最大数和最小数处理的共同部分就是尽量往低位或是高位填最大数,那么直接先求出最大数,再翻转过来稍作处理便可得到最小数。考虑到最高为可能是0,在此种情况下,只需要在最小数中的有值最高位借1,放到最高位即可。

另外就是要判断无解的情况,如S<=1或是全部填9还不够和或是刚好够的情况。

[NOIP2008]排座椅

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

上课的时候总有一些同学和前后左右的人交头接耳,这是令小学班主任十分头疼的一件事情。不过,班主任小雪发现了一些有趣的现象,当同学们的座次确定下来之后,只有有限的D对同学上课时会交头接耳。同学们在教室中坐成了M行N列,坐在第i行第j列的同学的位置是(i,j),为了方便同学们进出,在教室中设置了K条横向的通道,L条纵向的通道。于是,聪明的小雪想到了一个办法,或许可以减少上课时学生交头接耳的问题:她打算重新摆放桌椅,改变同学们桌椅间通道的位置,因为如果一条通道隔开了两个会交头接耳的同学,那么他们就不会交头接耳了。
请你帮忙给小雪编写一个程序,给出最好的通道划分方案。在该方案下,上课时交头接耳的学生对数最少。

容易想到,可以先求出同学之间的坐标,根据坐标的个数多少来选择最优解。值得注意的是,在原题中给出条件“输入数据保证最优方案的唯一性”,提示我们仅考虑给出的行列数即可,不存在如行走廊隔开人数分别为3,2,1,1,而却取3个行走廊的情况(这样cmp就需要考虑隔开同样人数的情况,比较麻烦)

错误点:别忘了,不是隔开对数最多的走廊才排到前面,最后是按序号来排的!!!

#include
#include
#include
#include
#include
using namespace std;
struct ty{
	int pos,num;
}heng[1200],zong[1200];
int n,m,k,l,d,hn,zn;
//m横向列数,n纵向列数,k横向通道,l纵向通道 
bool cmp(ty x,ty y){
//	if(x.num==y.num) return x.posy.num;
}
bool cmp1(ty x,ty y){
	return x.pos>m>>n>>k>>l>>d;
	int rec=max(m,n);
	for(int i=1;i<=rec;++i)
		heng[i].pos=i,zong[i].pos=i,heng[i].num=0,zong[i].num=0;
	for(int i=1;i<=d;++i){
		int x,y,p,q;
		scanf("%d%d%d%d",&x,&y,&p,&q);
		if(x==p) zong[min(y,q)].num++; 
		else heng[min(x,p)].num++;
	}
	sort(heng+1,heng+1+m,cmp);
	sort(zong+1,zong+1+n,cmp);
	sort(heng+1,heng+1+k,cmp1);
	sort(zong+1,zong+1+l,cmp1);
	for(int i=1;i<=k;++i) cout<

矩阵消除游戏

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

牛妹在玩一个名为矩阵消除的游戏,矩阵的大小是n行m列(1<=n,m<=15),第i行第j列的单元格的权值为ai,j​,牛妹可以进行k个回合的游戏,在每个回合,牛妹可以选择一行或者选择一列,然后将这一行或者这一列的所有单元格中的权值变为0,同时牛妹的分数会加上这一行或者这一列中的所有单元格的权值的和。

牛妹想最大化她的得分,球球你帮帮她吧!

由n,m的数据范围可知,暴力枚举即可解决此题。由于不能根据最大值判断要选哪一行或列(如:400  1  1  100

           0      0  0    0

           0      0  0    0

           400  1  1    1        ,若有两次选择机会,显然应该选第一行和第四行,但一开始就选择最大值就会先选第一列,导致最后不能选择所有的数)

可以先枚举要选取的行数,再根据选择贪心。此处枚举选取行数,又可以用上前面讲过的二进制01串法。

#include
#include
#include
#include
using namespace std;
int n,m,k,a[20][20];
int lie[20],cnt1;//cnt1有多少格子被选中 
int cal(int st){
	int sum=0;
	for(int i=1;i<=n;++i){
		if(((st>>(i-1))&1)==1){
			for(int j=1;j<=m;++j)
				sum+=a[i][j];
			cnt1++; 
		}else{
			for(int j=1;j<=m;++j)
				lie[j]+=a[i][j];
		}
	}
	return sum;
}
int main(){
	int sum=0;
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j){
			scanf("%d",&a[i][j]);
			sum+=a[i][j];
		}
	if(k>=m||k>=n){
		cout<m) continue;
		sort(lie+1,lie+1+m);
		for(int i=1,j=m;i<=rest;++i,--j){
			sum+=lie[j];
		}
		ans=max(ans,sum);
	}
	cout<

区间覆盖(工作安排)

在0到L的数轴上由n个区间[li,ri],现在需要选出其中尽量多个区间,使得其两两不相交(n<=10^5)。

感性认知:选最短的?但存在“品”字形的反例(上面的“口”比下面两个“口”都短时)。不管怎样,一定要给后方的区间留足够多“余地”,使得我们的选择尽可能多。没错,这个“最大余地”就是使右区间尽量小。

理性证明:1、空集是在上述规则下产生的最优选择的子区间。2、利用数学归纳法证明,假设区间集合P={p1,p2,...,pi-1}为最优选择的子区间,又有与P不相交的区间pi和pi',且pi的右端点

活动安排

给n个活动,每个活动需要一段时间ci来完成,并且有一个截止时间di,当完成时间ti大于截止时间完成时,会扣除ti-di分,让你找出如何使自己所扣分的最大值最小(n<=10^5)。

不妨假设有事件a和事件b为两个连续发生的事件,设a在前比b在前更优。由于a、b两个互换并不会影响其他事件的发生,因此以此为基础可以推导出其他事件的发生。设ci为事件i花费的时间,di为事件i的截止时间,则:

a在前时,a扣分的最大值 s_{1}=max(\sum c_{i}+c_{a}-d_{a},0),b扣分的最大值 s_{2}=max(\sum c_{i}+c_{a}+c_{b}-d_{b},0);b在前时,a扣分的最大值 s_{1}'=max(\sum c_{i}+c_{b}+c_{a}-d_{a},0),b扣分的最大值为 s_{2}'=max(\sum c_{i}+c_{b}-d_{b},0)。由于a在前比b在前更优,即 max(s_{1},s_{2})\leq max(s_{1}',s_{2}'),又易知 s_{1}<s_{1}'s_{2}>s_{2}',故需 s_{2}<s_{1}' 才可使等式成立,即 \sum c_{i}+c_{a}+c_{b}-d_{b}\leq\sum c_{i}+c_{b}+c_{a}-d_{a},得 d_{a}\leq d_{b}。所以结束时间越早,越应该先完成。

事实上,对所有交换两变量对整体无影响的题目,均可以用上述方法进行推导。

[NOIP2012]国王的游戏

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

恰逢 H 国国庆,国王邀请 n 位大臣来玩一个有奖游戏。首先,他让每个大臣在左、右手上面分别写下一个整数,国王自己也在左、右手上各写一个整数。然后,让这 n 位大臣排成一排,国王站在队伍的最前面。排好队后,所有的大臣都会获得国王奖赏的若干金币,每位大臣获得的金币数分别是:排在该大臣前面的所有人的左手上的数的乘积除以他自己右手上的数,然后向下取整得到的结果。
国王不希望某一个大臣获得特别多的奖赏,所以他想请你帮他重新安排一下队伍的顺序,使得获得奖赏最多的大臣,所获奖赏尽可能的少。注意,国王的位置始终在队伍的最前面。

1<=n<=1000,0

同上题,可设a在b前更优(即最大值最更小),令li为左手数、ri为右手数,通过列式得当a在b前的式子和b在a前的式子,最终可以求得la*ra<=lb*rb,此为排序条件。由于数太大,需要使用异或来解决问题。

[USACO 2007 Jan S]Protecting the Flowers

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网
 

题目描述

农夫有n头牛跑到花田上吃草,农夫要把他们送回自己的牛舍,所花的时间分别为ti(单程时间为ti),每头牛留在花田上单位时间吃花量分别为di,则花田上花被破坏的最少数量为多少?

显然,单位时间吃花更多的应该尽早送回去。用数学式子推导也可得同样结论。

你可能感兴趣的:(贪心算法,算法)