题解 贪心专题(2.1-2.7) 之 A-D

378 阅读4分钟

声明:

题目来源:vjudge.net/contest/421…

此题解不一定所有题目都是本人独立完成,很有可能借鉴他人的算法,但代码一定由本人编写,决不复制粘贴。

题目A:Wooden Sticks

There is a pile of n wooden sticks. The length and weight of each stick are known in advance. The sticks are to be processed by a woodworking machine in one by one fashion. It needs some time, called setup time, for the machine to prepare processing a stick. The setup times are associated with cleaning operations and changing tools and shapes in the machine. The setup times of the woodworking machine are given as follows:

(a) The setup time for the first wooden stick is 1 minute.

(b) Right after processing a stick of length l and weight w , the machine will need no setup time for a stick of length l' and weight w' if l<=l' and w<=w'. Otherwise, it will need 1 minute for setup.

You are to find the minimum setup time to process a given pile of n wooden sticks. For example, if you have five sticks whose pairs of length and weight are (4,9), (5,2), (2,1), (3,5), and (1,4), then the minimum setup time should be 2 minutes since there is a sequence of pairs (1,4), (3,5), (4,9), (2,1), (5,2).

The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case consists of two lines: The first line has an integer n , 1<=n<=5000, that represents the number of wooden sticks in the test case, and the second line contains n 2 positive integers l1, w1, l2, w2, ..., ln, wn, each of magnitude at most 10000 , where li and wi are the length and weight of the i th wooden stick, respectively. The 2n integers are delimited by one or more spaces.

The output should contain the minimum setup time in minutes, one per line.

数据范围:T unknown. n<=5000. l, w<=10000.

输入样例:

3 
5 
4 9 5 2 2 1 3 5 1 4 
3 
2 2 1 1 2 2 
3 
1 3 2 2 3 1

输出样例:

2
1
3

题解思路:

此处先定义n阶递增:

首先,题目可以简化为将已知结构体组,拆分为最少二阶递增子列数。

结构体如下:

struct stick{
	int l,w;  //length and weight
	bool vis;  //being counted or not
};

将结构体组先按照length排序,事实上,按照weight同样可以。

之后,循环统计排序后weight的上升子列数,统计过的结构体将vis的值赋为false。

题解:

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

struct stick{  //define a struct
	int l,w;
	bool vis;
};

bool compare(stick a,stick b){
	if(a.l==b.l) return a.w<b.w;
	return a.l<b.l;
}

int main(){
	int t,n,r,n_;
	stick a[5007];
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%d%d",&a[i].l,&a[i].w);
			a[i].vis=true;
		}
		sort(a,a+n,compare);
		r=0;  //use r to calculate the result
		n_=0;  //hereby, use n_ to judge whether all sticks are counted
		int flag=0;
		while(n_!=n){
			flag=0;  //use flag to judge whether l'<=l
			for(int i=0;i<n;i++){
				if(a[i].vis&&a[i].w>=flag){
					a[i].vis=false;
					flag=a[i].w;
					n_++;
				}
			}
			r++;
		} 
		printf("%d\n",r);
	}
	return 0;
}

题目B:Game Prediction

Suppose there are M people, including you, playing a special card game. At the beginning, each player receives N cards. The pip of a card is a positive integer which is at most N*M. And there are no two cards with the same pip. During a round, each player chooses one card to compare with others. The player whose card with the biggest pip wins the round, and then the next round begins. After N rounds, when all the cards of each player have been chosen, the player who has won the most rounds is the winner of the game.

Given your cards received at the beginning, write a program to tell the maximal number of rounds that you may at least win during the whole game.

The input consists of several test cases. The first line of each case contains two integers m (2 <= m <= 20) and n (1 <= n <= 50), representing the number of players and the number of cards each player receives at the beginning of the game, respectively. This followed by a line with n positive integers, representing the pips of cards you received at the beginning. Then a blank line follows to separate the cases.

The input is terminated by a line with two zeros.

For each test case, output a line consisting of the test case number followed by the number of rounds you will at least win during the game.

数据范围:2<=m<=20, 1<=n<=50.

输入样例:

2 5
1 7 2 10 9
6 11
62 63 54 66 65 61 57 56 50 53 48
0 0

输出样例:

Case 1: 2
Case 2: 4

题解思路:

有M个人,N张卡,卡的点数(points)范围为1到M*N。

在最坏的情况下,对于你的每一张牌,只要有更大的点数在其他玩家手中,这张牌将被击败。

例如样例1,你的手牌为1 2 7 9 10,对方手牌为3 4 5 6 8

你的手牌9和10一定可以胜利,对于7,对方的8可以击败,1和2必败,所以最少可以赢2局。 

注意以下情况:

你的手牌为1 2 7 8 9,对方手牌为3 4 5 6 10,你的手牌7 8 9只会被10击败一个,所以最少还可以赢两局,玩家多于2时,也可以当做只有你和另一个玩家持有除你之外的其他牌来处理。

问题可以转化为:寻找最少的数对,使得数对中你的点数大于对方点数。

具体做法为向下遍历,将对方尽肯能大的点数与你的尽可能大的点数配对且满足你的点数小于对方点数。

题解:

#include<iostream>
using namespace std;

int p[1007];  
/*
1 have this pip
2 haven't this pip
3 have this pip but being defeated
4 have this pip but could win
*/

int main(){
	int m,n,round=1,flag=0,tem;
	int c=0;
	while(true){
		scanf("%d%d",&m,&n);
		if(m==0){
			return 0;
		}
		flag=0;
		c=0;
		for(int i=0;i<=1007;i++){  //Initilization
			p[i]=2;  //you don't have any card.
		}
		for(int i=0;i<n;i++){
			scanf("%d",&tem);
			p[tem]=1;  //give you card with point 'tem'
		}
		for(int i=m*n;i>0;i--){
			if(p[i]==2){  //you don't have it and it is bigger than yours
				flag++;
			}
			if(p[i]==1){  //you have it
				if(flag>0){  //are there any card bigger than yours?
					p[i]=3;  //being defrated
					flag--;  //the number of cards bigger than yours minus 1
				}
				else{
					p[i]=4;  //not being defeated
				}
			}
			if(p[i]==4){  //if your card was not being defeated, the result plus 1
				c++;
			}
		}
		printf("Case %d: %d\n",round,c);
		round++;
	}
}

题目C:Doing Homework Again

Ignatius has just come back school from the 30th ACM/ICPC. Now he has a lot of homework to do. Every teacher gives him a deadline of handing in the homework. If Ignatius hands in the homework after the deadline, the teacher will reduce his score of the final test. And now we assume that doing everyone homework always takes one day. So Ignatius wants you to help him to arrange the order of doing homework to minimize the reduced score.

The input contains several test cases. The first line of the input is a single integer T that is the number of test cases. T test cases follow.
Each test case start with a positive integer N(1<=N<=1000) which indicate the number of homework.. Then 2 lines follow. The first line contains N integers that indicate the deadlines of the subjects, and the next line contains N integers that indicate the reduced scores.

For each test case, you should output the smallest total reduced score, one line per test case.

数据范围:T unknown, 1<=N<=1000.

输入样例:

3
3
3 3 3
10 5 1
3
1 3 1
6 2 3
7
1 4 6 4 2 4 3
3 2 1 7 6 5 4

输出样例:

0
3
5

题解思路:

把作业按照分数优先从大到小排序,分数相同则时间较早的在前。

创建一个数组记录某一时间做的作业,空闲则为-1,一个bool数组记录某个作业是否被做。

按顺序将作业放入,先找截止时间位置,若被占用(不为-1),则向前寻找,若寻找到1时间仍不是空闲,则不作这个作业,做过的作业在bool数组里记录。

将每一份作业按照如上方式遍历之后,把bool数组里记录的未做的作业分数相加,得到总扣分,输出。

另,网上有并查集做法,我没有看懂,这里采用的是贪心策略,先安排分数高的作业。

题解:

#include<iostream>
#include<algorithm>
#include<string.h>
using namespace std;

struct deadline{
	int time,score;
}p[1007];

int a[1007];  //记录何时做何份作业 a[8]=11表示在8时间做了作业11
bool vis[1007];  //记录是否做了某份作业

bool compare(deadline a,deadline b){
	return a.score==b.score?a.time<b.time:a.score>b.score;
}

int main(){
	int t,n,r;
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		for(int i=0;i<n;i++){
			scanf("%d",&p[i].time);
		}
		for(int i=0;i<n;i++){
			scanf("%d",&p[i].score);
		}
		sort(p,p+n,compare);
		
		r=0;
		memset(a,-1,sizeof(a));  //未安排任何作业
		memset(vis,false,sizeof(vis));  //所有作业未做
		
		for(int i=0;i<n;i++){  //按分数大小遍历安排每一份作业的时间
			
			int j=p[i].time;  //从截止时间开始遍历 寻找空闲
			while(j>0&&!vis[i]){
				if(a[j]==-1){  //j时刻空闲
					a[j]=i;  //占用
					vis[i]=true;  //该作业被做
				}
				j--;
			}
		}
		
		for(int i=0;i<n;i++){
			if(!vis[i]){
				r+=p[i].score;  //累加未做的作业分数
			}
		}
		
		printf("%d\n",r);
	}
	return 0;
}

题目D:今年暑假不AC

“今年暑假不AC?”
“是的。”
“那你干什么呢?”
“看世界杯呀,笨蛋!”
“@#$%^&*%...”

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

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

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

数据范围:n<=100, Ti_s, Ti_e unknown.

输入样例:

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

输出样例:

5

题解思路:

尽可能看更多节目,那么如果两个节目开始时间相同,要选结束时间早的,结束时间相同,要选开始时间晚的。

我们先把电势节目按照结束时间从小到大排序,结束时间相同的节目,开始时间晚的在前。

然后遍历,如果当前空闲,则观看电视节目,如果下一个电视节目的开始时间早于当前节目的结束时间,则跳过。

这样可以保证看到的节目是剩下的节目中结束时间最早的,并且是开始时间最晚的。

题解:

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

struct tvshow{
	int st,et;
}p[107];

bool compare(tvshow a,tvshow b){
	if(a.et==b.et) return a.st>b.st;
	return a.et<b.et;
}

int main(){
	int n,flag,r;
	while(true){
		scanf("%d",&n);
		if(n==0){
			return 0;
		}
		
		for(int i=0;i<n;i++){
			scanf("%d%d",&p[i].st,&p[i].et);
		}
		sort(p,p+n,compare);
                
		r=0;
		flag=0;
		for(int i=0;i<n;i++){
			if(p[i].st>=flag){
				r++;
				flag=p[i].et;
			}
		}
		printf("%d\n",r);
	}
}