贪心(1)排序问题

目录

一,贪心算法

1、贪心选择性质

2、最优子结构性质

二,田忌赛马

CSU 1722 Race

力扣 870. 优势洗牌

HDU 1338 Game Prediction

HDU 1052、POJ 2287、UVA 1344、ZOJ 2397、FZU 1316、OpenJ_Bailian 2287、NBUT 1101  Tian Ji -- The Horse Racing

三,活动安排问题

CSU 1065 Scientific Conference

CodeForces 589F 究竟能吃多久?

HDU 1789 Doing Homework again

HDU 4864 Task

HDU 1051 Wooden Sticks

力扣 621. 任务调度器

力扣 1953. 你可以工作的最大周数

力扣 630. 课程表 III

四,其他排序问题

CSU 1009 抛硬币

CSU 1044 扑克排序

CSU 1254 开机

CSU 1270 Swap Digits

UVA 11292 Dragon of Loowater

力扣 484. 寻找排列

力扣 767. 重构字符串

力扣 1054. 距离相等的条形码

力扣 1402. 做菜顺序


一,贪心算法

贪心算法,又叫贪婪算法,是一种通过局部最优解求出全局最优解的算法,只有特定问题才能用贪心算法。

能够用贪心算法的问题,具备2个特征:

1、贪心选择性质

一个问题的整体最优解可通过一系列局部的最优解的选择达到,并且每次的选择可以依赖以前作出的选择,但不依赖于后面要作出的选择。

2、最优子结构性质

当一个问题的最优解包含其子问题的最优解。

二,田忌赛马

田忌赛马出自《史记》,孙膑先以下等马对齐威王的上等马,第一局田忌输了。第二场比赛孙膑拿上等马对齐威王的中等马,获胜了一局。第三局比赛,孙膑拿中等马对齐威王的下等马,又战胜了一局。比赛的结果是三局两胜,田忌赢了齐威王。 还是同样的马匹,由于调换一下比赛的出场顺序,就得到转败为胜的结果。

CSU 1722 Race

题目:

Description

Johnson and Bob are crazy motorcycle collecting enthusiasts. In order to prove their own motorcycles is the best, they issued a challenge to each other. Johnson wants to win the challenge.As a good friend of Johnson’s, you need to give advice to Johnson. Johnson and Bob each can select their best n motorcycles. And each motorcycle has a value v. There are n matches in total. Any motorcycle can be chosen optionally to participate in the match(but each motorcycle can only participate once). Each time the motorcycle with high value will win. Johnson gets the order of Bob’s motorcycles in advance. Can you help Johnson arrange the competition order of motorcycles in order to win the most of the matches?

Input

First line input an integer T(mean there are T cases)
In each case , first line input an integer n (mean there are n motorcycles) (0<n<=10000)
Next line input n integers (mean the value of Johnson’s n motorcycles)
Next line n integers (mean the value of Bob’s n motorcycles )

Output

Every case output an integer mean the most match can win.

Sample Input

1
5
6 4 5 1 3
8 9 3 4 7

Sample Output

2

题目的大意是有2个人 Johnson and Bob,先输入的是 Johnson 的,而且他知道Bob 的数字出场顺序。
现在要Johnson安排自己的顺序,1对1PK,使得赢的最多。
这个问题相当于Johnson可以控制2人的数字出场顺序,这样的话,就是用贪心来做了。
具体的贪心策略就是那个很简单的while循环。
当然,要首先进行排序。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
    int n;
    cin >> n;
    int t;
    while (n--)
    {
        cin >> t;
        int *a = new int[t];
        int *b = new int[t];
        for (int i = 0; i < t; i++)cin >> a[i];
        for (int i = 0; i < t; i++)cin >> b[i];
        sort(a, a + t);
        sort(b, b + t);
        int i = 0, j = 0;
        int sum = 0;
        while (i < t && j < t)
        {
            if (a[i]>b[j])
            {
                sum++;
                j++;
            }
            i++;
        }
        cout << sum << endl;
    }
    return 0;
}

力扣 870. 优势洗牌

题目:

给定两个大小相等的数组 A 和 B,A 相对于 B 的优势可以用满足 A[i] > B[i] 的索引 i 的数目来描述。

返回 A 的任意排列,使其相对于 B 的优势最大化。

示例 1:

输入:A = [2,7,11,15], B = [1,10,4,11]
输出:[2,11,7,15]
示例 2:

输入:A = [12,24,8,32], B = [13,25,32,11]
输出:[24,32,8,12]
 

提示:

1 <= A.length = B.length <= 10000
0 <= A[i] <= 10^9
0 <= B[i] <= 10^9

思路:

田忌赛马

对于B的任意序遍历,每次找A中比B[i]大的最小的A[j],如果没有就选最小的A[k]

代码:

class Solution {
public:
	vector<int> advantageCount(vector<int>& A, vector<int>& B) {
		vector<int>ans;
		multiset<int>mset;
		for (int i = 0; i < A.size(); i++) {
			mset.insert(A[i]);
		}
		for (int i = 0; i < B.size(); i++) {
			auto it = mset.upper_bound(B[i]); //找比B[i]大的最小A[*]
			if (it == mset.end())it = mset.begin(); //如果没有就取A[]中最小数
			ans.push_back(*it);
			mset.erase(it);
		}
		return ans;
	}
};

HDU 1338 Game Prediction

题目:

Description

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. 

Input

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. 

Output

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. 

Sample Input

2 5
1 7 2 10 9

6 11
62 63 54 66 65 61 57 56 50 53 48

0 0

Sample Output

Case 1: 2
Case 2: 4

这个题目其实就是问,在其他人都使用最优策略的情况下,我们最多能赢多少局。

首先,那些人的牌怎么分配对我们的结果没有任何影响。

可以等价的考虑成,剩下所有的牌都在1个人手中,n次对局,每次他出1张,我出1张,问我最少能赢多少局。

其次,这是一个田忌赛马问题。

因为要求我最少能赢多少局,所以这完全等价于一个完全透明的博弈,即他知道我每次要出什么牌。

更进一步,这等价于,我直接按照从大到小的顺序出牌,他知道这一点,而且知道我每次要出的是多少,

所以他会用田忌赛马的策略来压我。

他的策略,具体说来就是,每次看看能不能赢我,能赢我就出最大的牌赢我,不能的话就出最小的牌潇洒输掉。

以下简称大牌和小牌(只需要对他出的牌分2类,而不需要详细知道到底是多少)

当然,实际上在计算本题的时候,我们不需要把他每次出的牌算出来,只需要一个变量fail

fail表示我已经输了几局了,也就代表着他在这之前已经出了几张大牌了,所以fail初始化为0。

下面我解释一下输赢指标:i + list[i] + fail < m*n

第i局(从第0局开始),我出的是list[i],他已经出过fail个大牌了,这些大牌都比list[i]大

请注意,虽然这个地方的推理用到了我是按照顺序出牌的假设,但是这个假设仍然具备一般性,仍然可以得到准确的答案。

现在我需要判断,他是否还有比list[i]大的牌,如果有,那么第i局是我输,fail++

怎么判断呢?

除掉我已经出的i张牌,和这fail张大牌,剩下的最大的牌就是m*n-i-fail

如果list[i]就是这个牌,那么我就是最大的,我赢,如果list[i]比这个牌小,那么这个牌一定还在他的手上,我输。

请注意!!!我说的很严谨!!!

一定要注意,小牌不一定都比list[i]小,可能我有些牌比较小,最后我出的牌比他出的一些小牌还小。

这种情况是很可能存在的,但是不影响我以上所有论述,也不影响下面代码的完全正确性,这一点,请自行辨析。

代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
int list[51];
bool cmp(int a, int b)
{
	return a > b;
}
 
int main()
{
	int m, n, fail, cas = 1;
	while (scanf("%d%d", &m, &n))
	{
		if (m == 0 && n == 0)break;
		for (int i = 0; i < n; i++)scanf("%d", &list[i]);
		sort(list, list + n, cmp);
		fail = 0;
		for (int i = 0; i < n; i++)if (i + list[i] + fail < m*n)fail++;
		printf("Case %d: %d\n", cas++, n - fail);
	}
	return 0;
}

0ms 的AC

HDU 1052、POJ 2287、UVA 1344、ZOJ 2397、FZU 1316、OpenJ_Bailian 2287、NBUT 1101  Tian Ji -- The Horse Racing

有一个非常像的题目,CSU 1722 Race(田忌赛马)

CSU上面的那个题目只求胜的场数的最大值,输和平局不做区分,所以比较简单。

而本题平局不扣分,输要扣分,所以复杂一些。

题目:

Description

Here is a famous story in Chinese history.

That was about 2300 years ago. General Tian Ji was a high official in the country Qi. He likes to play horse racing with the king and others.

Both of Tian and the king have three horses in different classes, namely, regular, plus, and super. The rule is to have three rounds in a match; each of the horses must be used in one round. The winner of a single round takes two hundred silver dollars from the loser.

Being the most powerful man in the country, the king has so nice horses that in each class his horse is better than Tian's. As a result, each time the king takes six hundred silver dollars from Tian.

Tian Ji was not happy about that, until he met Sun Bin, one of the most famous generals in Chinese history. Using a little trick due to Sun, Tian Ji brought home two hundred silver dollars and such a grace in the next match.

It was a rather simple trick. Using his regular class horse race against the super class from the king, they will certainly lose that round. But then his plus beat the king's regular, and his super beat the king's plus. What a simple trick. And how do you think of Tian Ji, the high ranked official in China?

Were Tian Ji lives in nowadays, he will certainly laugh at himself. Even more, were he sitting in the ACM contest right now, he may discover that the horse racing problem can be simply viewed as finding the maximum matching in a bipartite graph. Draw Tian's horses on one side, and the king's horses on the other. Whenever one of Tian's horses can beat one from the king, we draw an edge between them, meaning we wish to establish this pair. Then, the problem of winning as many rounds as possible is just to find the maximum matching in this graph. If there are ties, the problem becomes more complicated, he needs to assign weights 0, 1, or -1 to all the possible edges, and find a maximum weighted perfect matching...

However, the horse racing problem is a very special case of bipartite matching. The graph is decided by the speed of the horses -- a vertex of higher speed always beat a vertex of lower speed. In this case, the weighted bipartite matching algorithm is a too advanced tool to deal with the problem.

In this problem, you are asked to write a program to solve this special case of matching problem.

Input

The input consists of up to 50 test cases. Each case starts with a positive integer n ( n<=1000) on the first line, which is the number of horses on each side. The next n integers on the second line are the speeds of Tian's horses. Then the next n integers on the third line are the speeds of the king's horses. The input ends with a line that has a single `0' after the last test case.

Output

For each input case, output a line containing a single number, which is the maximum money Tian Ji will get, in silver dollars.

Sample Input

3
92 83 71
95 87 74
2
20 20
20 20
2
20 19
22 18
0

Sample Output

200
0
0

这个题目关键在于理清思路,找到完美的贪心策略。

定理一:如果田忌的最小数比齐王的最小数小,那么用田忌的最小数和齐王的最大数对战,输掉1轮,双方从n个数变成n-1个,一定可以通过求这个子问题的最优解得到n问题的最优解。

定理二:如果田忌的最小数比齐王的最小数大,那么用田忌的最小数和齐王的最小数对战,赢掉1轮,双方从n个数变成n-1个,一定可以通过求这个子问题的最优解得到n问题的最优解。

定理三:如果田忌的最大数比齐王的最大数小,那么用田忌的最小数和齐王的最大数对战,输掉1轮,双方从n个数变成n-1个,一定可以通过求这个子问题的最优解得到n问题的最优解。

定理四:如果田忌的最大数比齐王的最大数大,那么用田忌的最大数和齐王的最大数对战,赢掉1轮,双方从n个数变成n-1个,一定可以通过求这个子问题的最优解得到n问题的最优解。

定理五:如果田忌的最小数和齐王的最小数相同,而且田忌的最大数和齐王的最大数相同,那么用田忌的最小数和齐王的最大数对战,双方从n个数变成n-1个,一定可以通过求这个子问题的最优解得到n问题的最优解。

注意,定理五里面,田忌不一定会输掉这1轮。

定理一二和定理三四是独立的,也就是说,如果田忌的最小数比齐王的最小数小,而且最大数比齐王的最大数大,

那么不管是用田忌的最小数还是最大数和齐王的最大数对战,都可以得到最优解。

所有的情况都可以分为这5种种的至少一种,于是策略便清楚明了了。

因为定理三和定理五里面的策略其实是一样的,所以写的代码合起来了。代码只分了4类。

虽然定理一里面的策略也一样,不过不想把分类写的太奇怪,就没有合并。

代码:

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

int a[1000];
int b[1000];

int main()
{
	int n;
	while (cin >> n)
	{
		if (n == 0)break;
		for (int i = 0; i < n; i++)cin >> a[i];
		for (int i = 0; i < n; i++)cin >> b[i];
		sort(a, a + n);
		sort(b, b + n);
		int lowa = 0, lowb = 0, higha = n - 1, highb = n - 1, sum = 0;
		while (lowa <= higha)
		{
			if (a[lowa] < b[lowb])
			{
				lowa++;
				highb--;
				sum--;
			}
			else if (a[lowa]>b[lowb])
			{
				lowa++;
				lowb++;
				sum++;
			}
			else
			{
				if (a[higha]>b[highb])
				{
					higha--;
					highb--;
					sum++;
				}
				else
				{
					sum -= (a[lowa] < b[highb]);
					lowa++;
					highb--;
				}
			}
		}
		cout << sum * 200 << endl;
	}
	return 0;
}

三,活动安排问题

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

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;
}

注意,对于动态数组,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(背包)背包问题大全_jin ge jin qu hao-CSDN博客

我的想法是把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;
}

力扣 621. 任务调度器

题目:

给定一个用字符数组表示的 CPU 需要执行的任务列表。其中包含使用大写的 A - Z 字母表示的26 种不同种类的任务。任务可以以任意顺序执行,并且每个任务都可以在 1 个单位时间内执行完。CPU 在任何一个单位时间内都可以执行一个任务,或者在待命状态。

然而,两个相同种类的任务之间必须有长度为 n 的冷却时间,因此至少有连续 n 个单位时间内 CPU 在执行不同的任务,或者在待命状态。

你需要计算完成所有任务所需要的最短时间。

示例 1:

输入: tasks = ["A","A","A","B","B","B"], n = 2
输出: 8
执行顺序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B.
注:

任务的总个数为 [1, 10000]。
n 的取值范围为 [0, 100]。

代码:

class Solution {
public:
	int leastInterval(vector<char>& tasks, int n) {
		int num[26] = { 0 };
		for (auto it = tasks.begin(); it != tasks.end(); it++)
		{
			num[*it - 'A']++;
		}
		int maxNum = 0, k = 0, sumNum = 0;
		for (int i = 0; i < 26; i++)maxNum = max(maxNum, num[i]), sumNum += num[i];
		for (int i = 0; i < 26; i++)k += (maxNum == num[i]);
		return max((maxNum - 1)*(n + 1) + k, sumNum);
	}
};

力扣 1953. 你可以工作的最大周数

给你 n 个项目,编号从 0 到 n - 1 。同时给你一个整数数组 milestones ,其中每个 milestones[i] 表示第 i 个项目中的阶段任务数量。

你可以按下面两个规则参与项目中的工作:

  • 每周,你将会完成 某一个 项目中的 恰好一个 阶段任务。你每周都 必须 工作。
  • 在 连续的 两周中,你 不能 参与并完成同一个项目中的两个阶段任务。

一旦所有项目中的全部阶段任务都完成,或者仅剩余一个阶段任务都会导致你违反上面的规则,那么你将 停止工作 。注意,由于这些条件的限制,你可能无法完成所有阶段任务。

返回在不违反上面规则的情况下你 最多 能工作多少周。

示例 1:

输入:milestones = [1,2,3]
输出:6
解释:一种可能的情形是:
​​​​- 第 1 周,你参与并完成项目 0 中的一个阶段任务。
- 第 2 周,你参与并完成项目 2 中的一个阶段任务。
- 第 3 周,你参与并完成项目 1 中的一个阶段任务。
- 第 4 周,你参与并完成项目 2 中的一个阶段任务。
- 第 5 周,你参与并完成项目 1 中的一个阶段任务。
- 第 6 周,你参与并完成项目 2 中的一个阶段任务。
总周数是 6 。

示例 2:

输入:milestones = [5,2,1]
输出:7
解释:一种可能的情形是:
- 第 1 周,你参与并完成项目 0 中的一个阶段任务。
- 第 2 周,你参与并完成项目 1 中的一个阶段任务。
- 第 3 周,你参与并完成项目 0 中的一个阶段任务。
- 第 4 周,你参与并完成项目 1 中的一个阶段任务。
- 第 5 周,你参与并完成项目 0 中的一个阶段任务。
- 第 6 周,你参与并完成项目 2 中的一个阶段任务。
- 第 7 周,你参与并完成项目 0 中的一个阶段任务。
总周数是 7 。
注意,你不能在第 8 周参与完成项目 0 中的最后一个阶段任务,因为这会违反规则。
因此,项目 0 中会有一个阶段任务维持未完成状态。

提示:

  • n == milestones.length
  • 1 <= n <= 105
  • 1 <= milestones[i] <= 109
class Solution {
public:
	long long numberOfWeeks(vector<int>& milestones) {
		long long s = 0;
		int maxs = 0;
		for (auto x : milestones)s += x, maxs = max(maxs, x);
		return min(s, (s - maxs) * 2+1);
	}
};

力扣 630. 课程表 III

这里有 n 门不同的在线课程,他们按从 1 到 n 编号。每一门课程有一定的持续上课时间(课程时间)t 以及关闭时间第 d 天。一门课要持续学习 t 天直到第 d 天时要完成,你将会从第 1 天开始。

给出 n 个在线课程用 (t, d) 对表示。你的任务是找出最多可以修几门课。

示例:

输入: [[100, 200], [200, 1300], [1000, 1250], [2000, 3200]]
输出: 3
解释: 
这里一共有 4 门课程, 但是你最多可以修 3 门:
首先, 修第一门课时, 它要耗费 100 天,你会在第 100 天完成, 在第 101 天准备下门课。
第二, 修第三门课时, 它会耗费 1000 天,所以你将在第 1100 天的时候完成它, 以及在第 1101 天开始准备下门课程。
第三, 修第二门课时, 它会耗时 200 天,所以你将会在第 1300 天时完成它。
第四门课现在不能修,因为你将会在第 3300 天完成它,这已经超出了关闭日期。
 

提示:

整数 1 <= d, t, n <= 10,000 。
你不能同时修两门课程。

解题思路:

贪心,把课程按照d增序排序,逐步维护当前已选的课程列表,并用整数tsum记录他们的总时间。

依次读取每一个新课程,如果再加上这个课程,时间d没有超,即tsum+t<=d, 那么就加上,ans加1,

如果加上之后时间d超了,那么就用这个课程替换掉所有已选课程里面,t最大的那个课程,ans不变。

所以需要优先队列。

C语言实现方法:

用max型线段树,实现大顶堆的优先队列。

注意:这里的maxx数组记录的是最大元素的下标,query查询的返回结果也是下标。

struct anode
{
    int t;
    int d;
};
int n;
int num[10001],maxx[40001];
struct anode node[10001];
 
int cmpnode(const void* n1,const void* n2)
{
    return ((struct anode*)n1)->d > ((struct anode*)n2)->d;
}
 
void build(int key, int low, int high)//id
{
	if (low == high)
	{
		maxx[key] = low;
		return;
	}
	int mid = (low + high) / 2;
	build(key * 2, low, mid);
	build(key * 2 + 1, mid + 1, high);
	maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
 
void update(int key, int low, int high, int uplace)//id
{
	if (low == high)
	{
		maxx[key] = low;
		return;
	}
	int mid = (low + high) / 2;
	if (uplace <= mid)update(key * 2, low, mid, uplace);
	else update(key * 2 + 1, mid + 1, high, uplace);
	maxx[key] = (num[maxx[key * 2]] > num[maxx[key * 2 + 1]]) ? maxx[key * 2] : maxx[key * 2 + 1];
}
 
int query(int key, int low, int high, int x, int y)//id
{
	if (low == x && high == y)return maxx[key];
	int mid = (low + high) / 2;
	if (mid < x)return query(key * 2 + 1, mid + 1, high, x, y);
	if (mid >= y)return query(key * 2, low, mid, x, y);
	int a = query(key * 2, low, mid, x, mid);
	int b = query(key * 2 + 1, mid + 1, high, mid + 1, y);
	return  (num[a]>num[b]) ? a : b;
}
 
void push(int loc)
{
    num[loc]=node[loc].t; ///根据实际情况修改
    update(1,1,n,loc);
}
void pop(int loc)
{
    num[loc]=0; ///根据实际情况修改
    update(1,1,n,loc);
}
int top()//id
{
    return query(1,1,n,1,n);
}
 
int scheduleCourse(int** courses, int coursesSize, int* coursesColSize){
    n=coursesSize;
    memset(num,0,sizeof(num));
    build(1,1,n);    
    for(int i=1;i<=n;i++)node[i].t=courses[i-1][0],node[i].d=courses[i-1][1];
    qsort(node+1,n,sizeof(struct anode),cmpnode);
    int tsum=0,ans=0;
    for(int i=1;i<=n;i++)
    {        
        if(tsum+node[i].t<=node[i].d)
        {
            tsum+=node[i].t,ans++;
            push(i);
        }
        else if(node[i].t<node[top()].t)
        {
            tsum+=node[i].t - node[top()].t;
            pop(top());
            push(i);
        }
    }
    return ans;
}

四,其他排序问题

除了田忌赛马、活动安排问题这2类经典的排序类贪心问题之外,其他的排序类贪心问题。

CSU 1009 抛硬币

题目:

Description

James得到了一堆有趣的硬币,于是决定用这些硬币跟朋友们玩个小游戏。在一个N行M列的表格上,每一个第i行第j列的格子上都放有一枚James的硬币,抛该硬币正面朝上的概率为Pij,所有抛硬币事件两两之间是相互独立的。

现在,玩家在M列硬币中,从每一列里各选择1枚,共M枚,构成一组。如此重复选择N组出来,且保证被选择过的硬币不能再选。选好组之后,每组的M枚硬币各抛一次,如果都是正面朝上,则该组胜利,总分赢得1分;否则该组失败,总分不加也不减。请问,如果让你自行选择硬币的分组,游戏总得分的数学期望的最大值是多少?

Input

输入有多组数据。每组数据第一行为N和M,1≤N≤100,1≤M≤10,以空格分隔。接下来有N行,每行M个小数,表示表格中对应的Pij。

输入以N=M=0结束,这组数据不输出结果。

Output

对于每组数据,输出对应游戏总得分的数学期望的最大值,四舍五入精确至4位小数。每组数据的输出占一行。

Sample Input

2 3
1.0 1.0 1.0
0.5 0.4 0.3
0 0

Sample Output

1.0600

首先要理解题意,就是把每一列变成任意的顺序,使得各行所有数之积的和最大。

根据排序不等式,这是一个贪心问题,贪心策略是每一列都排序。

代码:

#include<iostream>
using namespace std;
 
int par(double *list, int p, int r)
{
    int i = p, j = r + 1;
    double x = list[p];
    while (true)
    {
        while (list[++i] < x && i < r);
        while (list[--j]>x);
        if (i >= j)break;
        double t = list[j];
        list[j] = list[i];
        list[i] = t;
    }
    list[p] = list[j];
    list[j] = x;
    return j;
}
 
void sort(double *list, int low, int high)
{
    if (low < high)
    {
        double p = par(list, low, high);
        sort(list, low, p - 1);
        sort(list, p + 1, high);
    }
}
 
int main()
{
    int n, m;
    while (cin >> n >> m)
    {
        if (n == 0 && m == 0)break;
        double **list = new double*[m];
        for (int i = 0; i < m; i++)list[i] = new double[n];
        for (int i = 0; i < n; i++)for (int j = 0; j < m; j++)cin >> list[j][i];
        for (int i = 0; i < m; i++)sort(list[i], 0, n - 1);
        double sum = 0;
        for (int i = 0; i < n; i++)
        {
            double t = 1;
            for (int j = 0; j < m; j++)t = t* list[j][i];
            sum += t;
        }
        printf("%.4lf\n", sum);
    }
    return 0;
}

CSU 1044 扑克排序

题目:

Description

一叠数值不同的扑克牌(不超过13张),每次只能拿最顶端的一张插入到任意位置。至少操作多少次,扑克牌能够从上到下是从大到小的顺序。

Input

 多组数据,每组第一行为n,扑克牌个数。第二行n个空格隔开的正整数,(这些数是1~13),为这叠扑克牌从下到上的数值。

Output

 每组数据输出一行,至少按规则操作的次数。

Sample Input

3
1 3 2
5
1 3 5 2 6

Sample Output

1
2

代码:

#include<iostream>
using namespace std;
 
int main()
{
	int n, a, b;
	while (cin>>n)
	{
		a = 0;
		bool flag = true;
		while (n--)
		{
			cin >> b;
			if (flag && a > b)
			{
				cout << n + 1 << endl;
				flag = false;
			}
			a = b;
		}
		if (flag)cout << 0 << endl;
	}
	return 0;
}

CSU 1254 开机

Description

机房有很多机器,不同机器开机时间不同。已知开始站在1号机器,从一台机器走到另一台机器需要5秒,如何才能用最短的时间打开所有的机器。

Input

每组数据开头一个n表示机器数,接下来n个数表示1~n号机器所需开机时间,以秒为单位。0 < n <= 1000,开机时间为10~60秒。

Output

 每组数据输出一行一个数,表示所有机器打开所需最短时间。

Sample Input

3
35
10
30

Sample Output

35

贪心

注意一开始是站在第一个机器

代码:

#include<iostream>
#include<algorithm>
using namespace std;
 
int main()
{
	int n, num[1000], ans;
	while (cin >> n)
	{
		for (int i = 0; i < n; i++)cin >> num[i];
		sort(num + 1, num + n);
		ans = num[0];
		for (int i = 1; i < n; i++)if (ans < num[i] + (n - i) * 5)ans = num[i] + (n - i) * 5;
		cout << ans << endl;
	}
	return 0;
}

CSU 1270 Swap Digits

题目:

Description

Now we have a number, you can swap any two adjacent digits of it, but you can not swap more than K times. Then, what is the largest probable number that we can get after your swapping?

Input

There is an integer T (1 <= T <= 200) in the first line, means there are T test cases in total.

For each test case, there is an integer K (0 <= K < 106) in the first line, which has the same meaning as above. And the number is in the next line. It has at most 1000 digits, and will not start with 0.

There are at most 10 test cases that satisfy the number of digits is larger than 100.

Output

For each test case, you should print the largest probable number that we can get after your swapping.

Sample Input

3
2
1234
4
1234
1
4321

Sample Output

3124
4213
4321

思路:贪心

首先尽量把最高位变成最大的数字,再往右一位位一样的操作,直到变换次数用完

代码:

#include<iostream>
#include<string.h>
using namespace std;
 
int main()
{
	int T, s, len;
	char ch[1001];
	cin >> T;	
	while(T--)
	{
		cin >> s >> ch;
		len = strlen(ch);
		for (int i = 0; i < len; i++)
		{
			if (s <= 0)break;
			char max = '0';
			int key;
			for (int j = i + 1; j < len && j <= i + s; j++)if (max < ch[j])max = ch[j], key = j;
			if (max > ch[i])
			{
				for (int j = key; j > i; j--)ch[j] = ch[j - 1];
				ch[i] = max, s -= key - i;
			}
		}
		cout << ch << endl;
	}
	return 0;
}

UVA 11292 Dragon of Loowater

题目:

贪心即可,直接用sort排序就可以了。

代码:

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

int ln[20000], lm[20000];

int main()
{
	int n, m, sum;
	while (cin >> n >> m)
	{
		if (n == 0)break;
		for (int i = 0; i < n; i++)cin >> ln[i];
		for (int i = 0; i < m; i++)cin >> lm[i];
		sort(ln, ln + n);
		sort(lm, lm + m);
		sum = 0;
		int i, j;
		for (i = 0, j = 0; i < n && j < m; j++)if (lm[j] >= ln[i])
		{
			sum += lm[j];
			i++;
		}
		if (i == n)cout << sum << endl;
		else cout << "Loowater is doomed!" << endl;
	}
	return 0;
}

力扣 484. 寻找排列

由范围 [1,n] 内所有整数组成的 n 个整数的排列 perm 可以表示为长度为 n - 1 的字符串 s ,其中:

  • 如果 perm[i] < perm[i + 1] ,那么 s[i] == 'I'
  • 如果 perm[i] > perm[i + 1] ,那么 s[i] == 'D' 。

给定一个字符串 s ,重构字典序上最小的排列 perm 并返回它。

示例 1:

输入: s = "I"
输出: [1,2]
解释: [1,2] 是唯一合法的可以生成秘密签名 "I" 的特定串,数字 1 和 2 构成递增关系。

示例 2:

输入: s = "DI"
输出: [2,1,3]
解释: [2,1,3] 和 [3,1,2] 可以生成秘密签名 "DI",
但是由于我们要找字典序最小的排列,因此你需要输出 [2,1,3]。

提示:

  • 1 <= s.length <= 105
  • s[i] 只会包含字符 'D' 和 'I'
class Solution {
public:
    vector<int> findPermutation(string s) {
        deque<int>q;
        map<int, int>visit, visit2;
        if (s[0] == 'I')q.push_back(0), visit[0] = 1;
        for (int i = 1; i < s.length(); i++) {
            if (s[i] == 'I' && s[i - 1] == 'D')q.push_back(i), visit[i] = 1;
        }
        if (s[s.length() - 1 == 'D'])q.push_back(s.length()), visit[s.length()] = 1;
        vector<int>ans(s.length() + 1);
        for (int i = 1; i <= ans.size(); i++) {
            int id = q.front();
            q.pop_front();
            ans[id] = i;
            visit2[id] = 1;
            if (id < ans.size() - 1) {
                int nid = id + 1;
                if (visit[nid] == 0 && (nid == ans.size() - 1 || s[nid] == 'I'|| visit2[nid+1]==1)) {
                    q.push_front(nid);
                    visit[nid] = 1;
                }
            }
            if (id > 0) {
                int nid = id - 1;
                if (visit[nid] == 0 && (nid == 0 || s[nid - 1] == 'D'||visit2[nid-1]==1)) {
                    q.push_front(nid);
                    visit[nid] = 1;
                }
            }
        }
        return ans;
    }
};

力扣 767. 重构字符串

题目:

给定一个字符串S,检查是否能重新排布其中的字母,使得两相邻的字符不同。

若可行,输出任意可行的结果。若不可行,返回空字符串。

示例 1:

输入: S = "aab"
输出: "aba"
示例 2:

输入: S = "aaab"
输出: ""
注意:

S 只包含小写字母并且长度在[1, 500]区间内。

思路:

分2步,先判断最多的字符是否超过其他所有字符的数量+1,如果超过就无法重构,不超过就可以重构。

其次,按照贪心原则,每次取数量最多的字符即可。

代码:

class Solution {
public:
	string reorganizeString(string S) {
		int num[26] = { 0 };
		int len = S.length();
		for (int i = 0; i < len; i++)num[S[i] - 'a']++;
		for (int i = 0; i < 26; i++)if (num[i]>(len + 1) / 2)return "";
		string ans = S;
		char tmp = '0';
		for (int i = 0; i < len; i++)
		{
			int maxNum = 0, maxId = 0;
			for (int j = 0; j < 26; j++)
			{
				if (tmp - 'a' == j)continue;
				if (maxNum < num[j])maxNum = num[j], maxId = j;
			}			
			ans[i] = tmp = 'a' + maxId;
			num[maxId]--;
		}
		return ans;
	}
};

力扣 1054. 距离相等的条形码

在一个仓库里,有一排条形码,其中第 i 个条形码为 barcodes[i]。

请你重新排列这些条形码,使其中任意两个相邻的条形码不能相等。 你可以返回任何满足该要求的答案,此题保证存在答案。

示例 1:

输入:barcodes = [1,1,1,2,2,2]
输出:[2,1,2,1,2,1]
示例 2:

输入:barcodes = [1,1,1,1,2,2,3,3]
输出:[1,3,1,3,2,1,2,1]
 

提示:

1 <= barcodes.length <= 10000
1 <= barcodes[i] <= 10000

思路一:

和力扣 767. 重构字符串一样,每次统计剩余的数中出现最多的。

但是不像字符只有26个,数太多了,所以需要用优先队列。

思路二:

其实只需要最开始统计一下所有数的出现次数,然后按照固定的id一次性全部填入即可。

这个固定的id序列就是奇偶序列,即0 2 4......1 3 5......

vector<int> fun(int n)
{
	vector<int>ans;
	for (int i = 0; i < n; i += 2)ans.push_back(i);
	for (int i = 1; i < n; i += 2)ans.push_back(i);
	return ans;
}

bool cmp(const pair<int, int>&a, const pair<int, int>&b)
{
	return a.second < b.second;
}


class Solution {
public:
	vector<int> rearrangeBarcodes(vector<int>& barcodes) {
		vector<int>ans(barcodes.size());
		vector<int>id = fun(barcodes.size());
		auto vp = Fshr(barcodes);
		sort(vp.begin(), vp.end(), cmp);
		barcodes = Funshr(vp);
		for (int i = 0; i < id.size(); i++)ans[id[i]] = barcodes[i];
		return ans;
	}
};

力扣 1402. 做菜顺序

一个厨师收集了他 n 道菜的满意程度 satisfaction ,这个厨师做出每道菜的时间都是 1 单位时间。

一道菜的 「 like-time 系数 」定义为烹饪这道菜结束的时间(包含之前每道菜所花费的时间)乘以这道菜的满意程度,也就是 time[i]*satisfaction[i] 。

返回厨师在准备了一定数量的菜肴后可以获得的最大 like-time 系数 总和。

你可以按 任意 顺序安排做菜的顺序,你也可以选择放弃做某些菜来获得更大的总和。

示例 1:

输入:satisfaction = [-1,-8,0,5,-9]
输出:14
解释:去掉第二道和最后一道菜,最大的 like-time 系数和为 (-1*1 + 0*2 + 5*3 = 14) 。每道菜都需要花费 1 单位时间完成。

示例 2:

输入:satisfaction = [4,3,2]
输出:20
解释:可以按照任意顺序做菜 (2*1 + 3*2 + 4*3 = 20)

示例 3:

输入:satisfaction = [-1,-4,-5]
输出:0
解释:大家都不喜欢这些菜,所以不做任何菜就可以获得最大的 like-time 系数。

提示:

  • n == satisfaction.length
  • 1 <= n <= 500
  • -1000 <= satisfaction[i] <= 1000
class Solution {
public:
    int maxSatisfaction(vector<int>& satisfaction) {
        sort(satisfaction.begin(),satisfaction.end());
        int s=0,ans=0;
        for(int i=satisfaction.size()-1;i>=0;i--){
            s+=satisfaction[i];
            if(s<=0)break;
            ans+=s;
        }
        return ans;
    }
};

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值