贪心应用解析(基础)例题:今年暑假不AC;Sorted Adjacent Differences;Cleaning Shifts

贪心

  1. 在对问题求解时,总作出当前看来是最好的选择。(也就是说,不从整体加以考虑,它所作出的仅仅是在某种意义上的局部最优解–是否是全局最优,需要证明)
  2. 贪心的条件:贪心选择性(局部最优解)+最优子结构
  3. 机试是很少证明(因为如果你自己证明出来,可能考试已经结束了)–因此一般就是想思路,找反例,然后进一步优化。

补充知识
sort:
algorithm 默认是从小到大

  • 对于int arr[10] 数组,sort(a, a+10);
  • 对于vector <int> v(10) , sort(v.begin(),v.end());
    传入了参数cmp后,根据cmp进行排序
bool cmp(int a, int b){//cmp函数返回的值是bool类型
	return a>b; //从大到小排列,不能写成含有等号的不等式,sort是不稳定的排序
}

结构体:

typedef struct Pat{
	int num;
	string sid;
	int age;
}PAT; //相当于struct Pat 
  
PAT pt[mMax] //定义一个结构体变量 mMax结构体数组的大小 

for(int i=0;i<n;i++){
		cout<<pt[i].sid<<endl; }//结构体的遍历

例题解析:

例题一:今年暑假不AC

试题内容:

“今年暑假不AC?”
“是的。”
“那你干什么呢?”
“看世界杯呀,笨蛋!”
“@#$%^&*%…”
确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。
作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)

input

输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n<=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1<=i<=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。

output

对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。

Sample Input

12
1 3
3 4
0 7
3 8
15 19
15 20
10 15
8 18
6 12
5 10
4 14
2 9
0

Sample Output

5

题目思路:先判断选哪个一个最优,再判断其他

1.可能开始最早的活动优先 ,反例:[0,100),[1,2][2,3],[3,4]。WA
2.可能最短的活动优先,反例:[0,5],[5,7],[7,8][8,12]。WA
3.可能结束最早的活动优先,这样就有利于让时间碎片化,让个数增加。

解题方法

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int maxn = 110;
struct jiemu {
	int l, r;
}a[maxn];
bool cmp(jiemu a, jiemu b) {
	if (a.r != b.r) return a.r < b.r;
	else return a.l > b.l;
}
int main() {
	int n;
	while (scanf("%d", &n) != EOF) {
		if (n == 0) break;
		for (int i = 0; i < n; i++)scanf("%d%d", &a[i].l, &a[i].r);
		sort(a, a + n, cmp);
		int cnt = 1;
		int end = a[0].r;
		for (int i = 1; i < n; i++) {
			if (a[i].l >= end) {
				cnt++;
				end = a[i].r;
			}
		}
		printf("%d\n", cnt);
	}

	return 0;
}
例题二:Sorted Adjacent Differences

试题内容:

You have array of n numbers a1,a2,…,an.

Rearrange these numbers to satisfy |a1−a2|≤|a2−a3|≤…≤|an−1−an|, where |x| denotes absolute value of x. It’s always possible to find such rearrangement.
Note that all numbers in a are not necessarily different. In other words, some numbers of a may be same.
You have to answer independent t test cases.

input

The first line contains a single integer t (1≤t≤104) — the number of test cases.
The first line of each test case contains single integer n (3≤n≤105) — the length of array a. It is guaranteed that the sum of values of n over all test cases in the input does not exceed 105.
The second line of each test case contains n integers a1,a2,…,an (−109≤ai≤109).

output

For each test case, print the rearranged version of array a which satisfies given condition. If there are multiple valid rearrangements, print any of them.

Sample Input

2
6
5 -2 4 8 6 5
4
8 1 4 2

Sample Output

5 5 4 6 8 -2
1 2 4 8

题意分析:

给定义一个a1,a2,…,an的序列,构造|a1−a2|≤|a2−a3|≤…≤|an−1−an|。(数字可能相等,其中a1与最后结果中的a1不是一一对应的)

题目思路:差值最大

排序然后依次差值排序,反例:但是有负数
排序后,最后一个与第一个,倒数第二个与第二个1 2 4 8 — 8142

解题方法

#include <cstdio>
#include <iostream>
#include <stack> 
#include <algorithm>
using namespace std;
const int mMax = 100010;
stack <int> ans; //本题我使用了差值最大,因此倒序输出,使用栈容器,先进后出
int t,a[mMax],n;

void result(int a[]){
	int flag = 0,i=n-1,j=0;
	while(i>=j){
		if(!flag){
			ans.push(a[i--]);
			flag = 1;
		}
		else{
			ans.push(a[j++]);
			flag = 0;
		}
	}
}
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%d",&a[i]);
		}
		sort(a,a+n);
		result(a);
		printf("%d",ans.top());
		ans.pop();
		while(ans.size()!=0){
			printf(" %d",ans.top());
			ans.pop();
		}//输出小技巧,要么第一个分开输出,要么最后一个分开输出
		printf("\n");
	}
	return 0;
} 
例题三:Cleaning Shifts

试题内容:

Farmer John is assigning some of his N (1 <= N <= 25,000) cows to do some cleaning chores around the barn. He always wants to have one cow working on cleaning things up and has divided the day into T shifts (1 <= T <= 1,000,000), the first being shift 1 and the last being shift T.
Each cow is only available at some interval of times during the day for work on cleaning. Any cow that is selected for cleaning duty will work for the entirety of her interval.
Your job is to help Farmer John assign some cows to shifts so that (i) every shift has at least one cow assigned to it, and (ii) as few cows as possible are involved in cleaning. If it is not possible to assign a cow to each shift, print -1.

input

  • Line 1: Two space-separated integers: N and T
  • Lines 2…N+1: Each line contains the start and end times of the interval during which a cow can work. A cow starts work at the start time and finishes after the end time.

output

  • Line 1: The minimum number of cows Farmer John needs to hire or -1 if it is not possible to assign a cow to each shift.

Sample Input

3 10
1 7
3 6
6 10

Sample Output

2

Hint

This problem has huge input data,use scanf() instead of cin to read data to avoid time limit exceed.
INPUT DETAILS:
There are 3 cows and 10 shifts. Cow #1 can work shifts 1…7, cow #2 can work shifts 3…6, and cow #3 can work shifts 6…10.
OUTPUT DETAILS:
By selecting cows #1 and #3, all shifts are covered. There is no way to cover all the shifts using fewer than 2 cows.

题意分析:

有t个片段,每个牛可以负责一段片段,求问最少多少头可以覆盖全部片段。若能,输出最小的数量,若不能则输出-1。

题目思路:区间排序

有序遍历牛的片段
特殊判断(端点值能否覆盖)
这道题的贪心最优解很容易想出来,但是计数部分逻辑性要强,比较繁琐。

解题方法

#include <cstdio>
#include <iostream>
#include <algorithm> 
using namespace std;
const int mMax = 25010;
typedef struct Cw{
	int start;
	int end;
}CW;
CW cw[mMax]; 
int n,t;
bool cmp(CW a,CW b){
	return a.start==b.start? a.end>b.end:a.start<b.start;
	//以开始值的降序排序,当开始值相同时,将结束值大的排在前面 
}
int counter(CW a[]){
	int r = a[0].end;
	int rmax = a[0].end;
	int i = 0,ans = 1;
	while(1){
		while(i+1<n&&a[i+1].start<=r+1){//一开始我交上去的时候写的是a[i+1].start<=r,修改之后AC了,但是其实题意还是依旧没有理解!!!下面讲了我的疑问,如果各路神仙有想法的话可以评论帮忙解答
			i++;
			if(a[i].end>rmax) rmax = a[i].end; //找到每一个的局部最优解 
		}
		if(rmax!=r){
			ans++;
			r = rmax;
		}
		else{
			if(i==n-1) break;
			else return -1;  //当前一个的结束值与后一个的开始值没有交集时,覆盖无效 
		}
	}
	return ans;
}
int main(){
	scanf("%d%d",&n,&t);
	int flag_l=1,flag_r=1;
	for(int i=0;i<n;i++){
		scanf("%d%d",&cw[i].start,&cw[i].end);
		if(cw[i].start==1) flag_l = 0;//端点值判断 
		if(cw[i].end==t) flag_r =0;
	}
	sort(cw,cw+n,cmp); 
	if(flag_l||flag_r) printf("-1\n");
	else{
		int an = counter(cw);
		printf("%d\n",an);
	}
	return 0;
}

疑问????

为什么是while(i+1<n&&a[i+1].start<=r+1),而不是while(i+1<n&&a[i+1].start<=r) 。
while(i+1<n&&a[i+1].start<=r+1),表示对于:
3 10
1 6
7 8
9 10 是存在最大数字3的,但是我表示疑惑对于6-7和8-9时间段不就没有牛进行工作了吗?这个不会违背题意吗?
如果各路神仙有想法的话,请求评论帮忙解答!!!!!

勉励:
打卡第二天,加油!ヾ(◍°∇°◍)ノ゙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值