贪心(1)田忌赛马

目录

一,贪心算法

1、贪心选择性质

2、最优子结构性质

二,田忌赛马

三,OJ实战

CSU 1722: Race(田忌赛马)

HDU 1338 Game Prediction(田忌赛马)

UVA-1344   HDU-1052    ZOJ-2397  POJ-2287  FZU-1316  OpenJ_Bailian-2287  NBUT-1101 Tian Ji -- The Horse Racing(田忌赛马)


一,贪心算法

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

扫描二维码关注公众号,回复: 12380458 查看本文章

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

1、贪心选择性质

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

2、最优子结构性质

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

二,田忌赛马

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

三,OJ实战

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

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

UVA-1344   HDU-1052    ZOJ-2397  POJ-2287  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;
}

猜你喜欢

转载自blog.csdn.net/nameofcsdn/article/details/112425560