贪心(2)活动安排问题

目录

一,活动安排问题

二,OJ实战

CSU 1065: Scientific Conference

CodeForces 589F 究竟能吃多久?

HDU 1789 Doing Homework again

HDU 4864 Task

HDU 1051 Wooden Sticks


一,活动安排问题

活动安排问题的本质和田忌赛马一样,本质上都是一个排序问题。

二,OJ实战

CSU 1065: Scientific Conference

题目:

Description

        Functioning of a scientific conference is usually divided into several simultaneous sections. For example, there may be a section on parallel computing, a section on visualization, a section on data compression, and so on.
       Obviously, simultaneous work of several sections is necessary in order to reduce the time for scientific program of the conference and to have more time for the banquet, tea-drinking, and informal discussions. However, it is possible that interesting reports are given simultaneously at different sections.
       A participant has written out the time-table of all the reports which are interesting for him. He asks you to determine the maximal number of reports he will be able to attend.

Input

        The first line contains the number 1 ≤ N ≤ 100000 of interesting reports. Each of the next N lines contains two integers Ts and Te separated with a space (1 ≤ Ts < Te ≤ 30000). These numbers are the times a corresponding report starts and ends. Time is measured in minutes from the beginning of the conference.

Output

        You should output the maximal number of reports which the participant can attend. The participant can attend no two reports simultaneously and any two reports he attends must be separated by at least one minute. For example, if a report ends at 15, the next report which can be attended must begin at 16 or later.

Sample Input

5
3 4
1 5
6 7
4 5
1 3

Sample Output

3

代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
struct node
{
	int s, e;
}nod[100005];
 
bool cmp(node a, node b)
{
	return a.e < b.e;
}
 
int main()
{
	int n, ans = 0, k = 0;
	cin >> n;
	for(int i=0;i<n;i++)cin >> nod[i].s >> nod[i].e;
	sort(nod, nod + n, cmp);
	for (int i = 0; i < n; i++)if (k < nod[i].s)k = nod[i].e, ans++;
	cout << ans;
	return 0;
}

CodeForces 589F 究竟能吃多久?

题目:

Description

A gourmet came into the banquet hall, where the cooks suggested n dishes for guests. The gourmet knows the schedule: when each of the dishes will be served.

For i-th of the dishes he knows two integer moments in time ai and bi (in seconds from the beginning of the banquet) — when the cooks will bring the i-th dish into the hall and when they will carry it out (ai < bi). For example, if ai = 10 and bi = 11, then the i-th dish is available for eating during one second.

The dishes come in very large quantities, so it is guaranteed that as long as the dish is available for eating (i. e. while it is in the hall) it cannot run out.

The gourmet wants to try each of the n dishes and not to offend any of the cooks. Because of that the gourmet wants to eat each of the dishes for the same amount of time. During eating the gourmet can instantly switch between the dishes. Switching between dishes is allowed for him only at integer moments in time. The gourmet can eat no more than one dish simultaneously. It is allowed to return to a dish after eating any other dishes.

The gourmet wants to eat as long as possible on the banquet without violating any conditions described above. Can you help him and find out the maximum total time he can eat the dishes on the banquet?

Input

The first line of input contains an integer n(1 ≤ n ≤ 100) — the number of dishes on the banquet.

The following n lines contain information about availability of the dishes. The i-th line contains two integers ai and bi(0 ≤ ai < bi ≤ 10000) — the moments in time when the i-th dish becomes available for eating and when the i-th dish is taken away from the hall.

Output

Output should contain the only integer — the maximum total time the gourmet can eat the dishes on the banquet.

The gourmet can instantly switch between the dishes but only at integer moments in time. It is allowed to return to a dish after eating any other dishes. Also in every moment in time he can eat no more than one dish.

Sample Input

Input

3
2 4
1 5
6 9

Output

6

Input

3
1 2
1 2
1 2

Output

0

Hint

In the first example the gourmet eats the second dish for one second (from the moment in time 1 to the moment in time 2), then he eats the first dish for two seconds (from 2 to 4), then he returns to the second dish for one second (from 4 to 5). After that he eats the third dish for two seconds (from 6 to 8).

In the second example the gourmet cannot eat each dish for at least one second because there are three dishes but they are available for only one second (from 1 to 2).

这个题目,也是学长说用贪心可以做之后,才做出来的。
虽然感觉和最佳教室安排问题好像,不过还是没想到,贪心策略居然是一模一样的,直接取结束时间最早的就可以了。
当然了,要排除掉吃的时间已经足够的菜。
深深感觉到自己还太弱了。
一开始思路很不清晰,最后才发现除了结点数组之外,不需要其他数组了。
而且ok函数直接用for循环来写比用递归简单多了。

代码:

#include<iostream>
using namespace std;
 
struct node		//d表示时间
{
	int a;
	int b;
	int d;
};
 
bool ok(int m, node *p, int n)		//m表示时间
{
	if (m == 0)return true;
	for (int i = 0; i < n; i++)p[i].d = m;
	for (int k = 0; k < 10000; k++)		//函数ok是非递归的,主要就是这个循环
	{
		int end = 10001, key = -1;		//最后一次修改是把10000改成了10001
		for (int i = 0; i < n; i++)
		{
			if (p[i].a <= k && p[i].b>k && p[i].d>0 && end > p[i].b)		/贪心策略//
			{
				end = p[i].b;
				key = i;
			}
		}
		if (key >= 0)p[key].d--;
	}
	for (int i = 0; i < n; i++)if (p[i].d)return false;
	return true;
}
 
int main()
{
	int n;
	while (cin >> n)
	{
		node *p = new node[n];
		int dif = 10000;
		for (int i = 0; i < n; i++)
		{
			cin >> p[i].a >> p[i].b;
			if (dif>p[i].b - p[i].a)dif = p[i].b - p[i].a;
		}
		int low = 0, high = dif;
		int mid;
		while (low +1 < high)		//二分查找答案
		{
			mid = (high + low) / 2;
			if (ok(mid,p,n))low = mid;
			else high = mid - 1;
		}
		if (ok(high, p, n))cout << high*n << endl;
		else cout << low*n << endl;
	}
	return 0;
}

HDU 1789 Doing Homework again

题目:

Description

zichen 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 zichen 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 zichen wants you to help him to arrange the order of doing homework to minimize the reduced score.

Input

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.

Output

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

Sample Input
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 

Sample Output

0
3

这个题目很明显是贪心的问题,而且不难看出,用贪心可以求出全局最优解。

我的思路是,按照代价(惩罚)score来排序,按照score降序的顺序依次安排作业到某一天准确的时间,如果安排不了就统计score的和。

每个作业尽量在截止日期那天做,如果那一天已经有了安排,就往前挪,如果没法挪了,那就丢弃(有惩罚的喔)。

比如说,对于sample 3,有7个作业,排序为(4,7)(2,6)(4,5)(3,4)(1,3)(4,2)(6,1)

首先,第4天做(4,7),然后,第2天做(2,6),然后,第3天做(4,5),然后,第1天做(3,4),

然后,(1,3)没法做,惩罚3,然后,(4,2)没法做,惩罚2,然后,(6,1)第6天做,一共惩罚5。

当然,如果遇到score相同的2个作业,应该是day大的排前面。

代码:

#include<algorithm>
#include<iostream>
#include<string.h>
using namespace std;
 
struct node
{
	int day;
	int score;
};
 
node nod[1000];
 
bool cmp(node a, node b)
{
	if (a.score > b.score)return true;
	if (a.score < b.score)return false;
	return a.day>b.day;
}
 
int main()
{
	int t,n;
	cin >> t;
	int max, sum;
	while (t--)
	{
		cin >> n;
		max = 0;
		for (int i = 0; i < n; i++)
		{
			cin >> nod[i].day;
			if (max < nod[i].day)max = nod[i].day;
		}
		for (int i = 0; i < n; i++)cin >> nod[i].score;
		sort(nod, nod + n, cmp);	
		int *list = new int[max+1];
		memset(list, 0, (max+1)*4);
		sum = 0;
		for (int i = 0; i < n; i++)
		{
			int j = nod[i].day;
			while (j > 0 && list[j])j--;
			if (j == 0)sum += nod[i].score;
			else list[j] = 1;
		}
		cout << sum << endl;
		delete list;
	}
	return 0;
}
<algorithm>
#include<iostream>
#include<string.h>
using namespace std;

struct node
{
	int day;
	int score;
};

node nod[1000];

bool cmp(node a, node b)
{
	if (a.score > b.score)return true;
	if (a.score < b.score)return false;
	return a.day>b.day;
}

int main()
{
	int t,n;
	cin >> t;
	int max, sum;
	while (t--)
	{
		cin >> n;
		max = 0;
		for (int i = 0; i < n; i++)
		{
			cin >> nod[i].day;
			if (max < nod[i].day)max = nod[i].day;
		}
		for (int i = 0; i < n; i++)cin >> nod[i].score;
		sort(nod, nod + n, cmp);	
		int *list = new int[max+1];
		memset(list, 0, (max+1)*4);
		sum = 0;
		for (int i = 0; i < n; i++)
		{
			int j = nod[i].day;
			while (j > 0 && list[j])j--;
			if (j == 0)sum += nod[i].score;
			else list[j] = 1;
		}
		cout << sum << endl;
		delete list;
	}
	return 0;
}

注意,对于动态数组,memset(list, 0, (max+1)*4); 长度不要用sizeof(list)因为list是指针,它的长度是4

而且,memset是对字节进行初始化的,(这也是为什么可以memset为-1,4个8位的-1组合起来就成了int的-1)

所以要加头文件 #include<string.h>

HDU 4864 Task

题目:

Description

Today the company has m tasks to complete. The ith task need xi minutes to complete. Meanwhile, this task has a difficulty level yi. The machine whose level below this task’s level yi cannot complete this task. If the company completes this task, they will get (500*xi+2*yi) dollars. 
The company has n machines. Each machine has a maximum working time and a level. If the time for the task is more than the maximum working time of the machine, the machine can not complete this task. Each machine can only complete a task one day. Each task can only be completed by one machine. 
The company hopes to maximize the number of the tasks which they can complete today. If there are multiple solutions, they hopes to make the money maximum.

Input

The input contains several test cases. 
The first line contains two integers N and M. N is the number of the machines.M is the number of tasks(1 < =N <= 100000,1<=M<=100000). 
The following N lines each contains two integers xi(0<xi<1440),yi(0=<yi<=100).xi is the maximum time the machine can work.yi is the level of the machine. 
The following M lines each contains two integers xi(0<xi<1440),yi(0=<yi<=100).xi is the time we need to complete the task.yi is the level of the task.

Output

For each test case, output two integers, the maximum number of the tasks which the company can complete today and the money they will get.

Sample Input

1 2
100 3
100 2
100 1

Sample Output

1 50004

有个类似的题目:HDU 3466 Proud Merchants(背包)https://blog.csdn.net/nameofcsdn/article/details/52093195

我的想法是把n个机器和m个作业task都按照降序排列,但是,

机器以y为第一关键字降序排列,以x为第一关键字降序排列,

作业以x为第一关键字降序排列,以y为第二关键字降序排列,即按照500*x+2*y排序

贪心策略是:

把作业从大到小安排机器,如果安排不了就不安排。

对于每个作业,选择不小于这个作业的最小机器。(什么叫最小机器,就是直接按照上面的排序来给出)

代码:

#include<iostream>
#include<string.h>
#include<algorithm>
using namespace std;
 
struct node
{
	int x;
	int y;
};
 
node noden[100005];
node nodem[100005];
int list[100005];	//list不为0表示对应的机器已经被选择
int section[101];	//section[i]是满足y不小于i的机器一共有多少个(对应最大下标)
 
bool cmpy(node a, node b)	//机器以y为第一关键字降序排列,以x为第一关键字降序排列
{
	if (a.y > b.y)return true;
	if (a.y < b.y)return false;
	return a.x>b.x;
}
 
bool cmpx(node a, node b)		//Task以x为第一关键字降序排列,以y为第二关键字降序排列
{
	if (a.x > b.x)return true;
	if (a.x < b.x)return false;
	return a.y>b.y;
}
 
int main()
{
	int n, m;
	while (cin >> n >> m)
	{
		for (int i = 0; i < n; i++)cin >> noden[i].x >> noden[i].y;
		for (int i = 0; i < m; i++)cin >> nodem[i].x >> nodem[i].y;
		sort(noden, noden + n, cmpy);	//机器
		sort(nodem, nodem + m, cmpx);	//Task
		memset(list, 0, sizeof(list));
		memset(section, 0, sizeof(section));
		for (int i = 0; i < n; i++)section[noden[i].y] = i + 1;
		for (int i = 99; i >= 0; i--)if (section[i] < section[i + 1])section[i] = section[i + 1];
		long long num = 0, sum = 0, t;
		for (int i = 0; i < m; i++)
		{
			t = nodem[i].y;
			for (int j = section[t] - 1; j >= 0; j--)
			{
				if (list[j] || noden[j].x < nodem[i].x)continue;
				list[j] = 1;
				num++;
				sum += nodem[i].x * 500 + t * 2;
				break;
			}
		}
		cout << num << " " << sum << endl;
	}
	return 0;
}

这个代码AC了,但是1326ms不够快。

下面,我开始阐述这个问题的本质,然后优化算法。

首先,作业毋庸置疑必须要这么排序,即优先选择500*x+2*y最大的作业。

这也就是说,不管所给数据是什么样的,只要优先给500*x+2*y大的作业安排合适的机器,就一定会得到最优解。

然后就只剩下一个问题了,对于一个作业来说,如何选择最合适的机器呢?

贪心策略无非就是,如果该作业是候选机器集不止一个元素,尽量选择某个元素,使得接下来待安排的作业不受影响。这样安排了的作业的数量就会最多。

所以说,问题可以抽离出这样一个小模型:

有2个作业和2个机器,作业A可以选择机器1和机器2,作业B只能选择机器1。

但是由于A优先安排机器,我们需要一个策略,能够准确而高效的判断,到底哪个机器是机器1,哪个机器是机器2?

请注意,在给作业A安排机器的时候,我们没有关于B的详细信息,所以这个时候还不知道作业B只能选择机器1。

模型的求解:

既然A可以选择机器2,而B不能,说明是因为B的y大于机器2的y(很显然B的x不超过A的x,A的x不超过机器2的x)

所以我们得到,机器1的y大于等于B的y,B的y大于机器2的y,即机器2的y小于机器1的y

(可能你觉得我这里又用到了作业B只能选择机器1这个条件,但是实际上我只是叙述的不够详细而已。

仔细辨别的话可以发现,我的求解没有问题。至少,对于这个独立的模型,很明显我的结论是对的)

模型的答案:

给A安排机器的时候,选择y较小的那个。

回到本题。

贪心策略其实很简单,不需要像上面那样。

给每个作业安排机器的时候,选择y最小的机器,如果不止一个,任选一个即可。

可能你已经发现,在上面的模型中,有的地方我写的是大于,有的地方我写的是大于等于。

我有自信,上面的模型不仅没有问题,而且很精准。

也就是说,仔细体会上面的模型,你就能明白,为什么如果y最小的机器不止一个,任选一个即可。

代码:

#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
 
struct node
{
	int x;
	int y;
};
 
node noden[100005];
node nodem[100005];
vector<int>v[101];
 
bool cmp(node a, node b)		//Task以x为第一关键字降序排列,以y为第二关键字降序排列
{
	if (a.x > b.x)return true;
	if (a.x < b.x)return false;
	return a.y>b.y;
}
 
int main()
{
	ios_base::sync_with_stdio(false);
	int n, m;
	vector< int >::iterator p;
	while (cin >> n >> m)
	{
		for (int i = 0; i <= 100; i++)v[i].clear();
		for (int i = 0; i < n; i++)
		{
			cin >> noden[i].x >> noden[i].y;
			v[noden[i].y].insert(v[noden[i].y].end(), i);
		}
		for (int i = 0; i < m; i++)cin >> nodem[i].x >> nodem[i].y;
		sort(nodem, nodem + m, cmp);	//Task
		long long num = 0, sum = 0;
		for (int i = 0; i < m; i++)
		{
			bool b = false;
			for (int j = nodem[i].y; j <= 100; j++)
			{
				if (b)break;
				for (p = v[j].begin(); p != v[j].end(); p++)
				{
					if (noden[*p].x >= nodem[i].x)
					{
						num++;
						sum += nodem[i].x * 500 + nodem[i].y * 2;
						v[j].erase(p);
						b = true;
						break;
					}
				}
			}
		}
		cout << num << " " << sum << endl;
	}
	return 0;
}

HDU 1051 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). 

Input

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. 

Output

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

Sample Input

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

Sample Output

2
1
3

思路:先排序,然后直接贪心就可以了

代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
struct nod
{
	int l, w;
	bool visit;
}node[5001];
 
bool cmp(nod a, nod b)
{
	if (a.l == b.l)return a.w<b.w;
	return a.l < b.l;
}
 
int main()
{
	int T, n, ans;
	cin >> T;
	while (T--)
	{
		cin >> n;
		for (int i = 1; i <= n; i++)
		{
			cin >> node[i].l >> node[i].w;
			node[i].visit = false;
		}
		sort(node + 1, node + n + 1, cmp);
		ans = 0;
		for (int i = 1; i <= n; i++)
		{
			if (node[i].visit)continue;
			ans++;
			int k = i;
			for (int j = i + 1; j <= n; j++)
			{
				if (node[j].visit)continue;
				if (node[k].w <= node[j].w)node[j].visit = true, k = j;
			}
		}
		cout << ans << endl;
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/nameofcsdn/article/details/112750825
今日推荐