这题有两种解法, 一种是通过动态规划弥补一个不太成熟的贪心思路, 使得最后的结果正确. 另一种方法是构造一个更好的贪心策略.
动态规划依托的结论: 每次给齐王最强的马匹配一匹田忌的马, 那么这匹马不是田忌最强的马就是最弱的马, 这一个不完善的贪心策略. 所以动态规划佐之.
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <iostream> using namespace std; /* 解法:开设状态dp[i][j]表示田忌出了i匹快马, j-i匹慢马, 齐王出了j匹慢马的情况下田忌 所能够赢到的最大场数. 有如下动态规划方程: dp[i][j] = max( dp[i-1][j-1] + judge(i, j), dp[i][j-1] + judge(j-i, j) ); 上面这个方程描绘了一个这样的场景:齐王按照从强到弱出马的顺序, 田忌每次进行拿 已经保存的最优值来决定这次该做何选择, 到底是拼了还是输一场以换取更大的胜利 */ int N, h1[1005], h2[1005], dp[1005][1005]; int judge(int a, int b) { if (h1[a] > h2[b]) return 1; else if (h1[a] < h2[b]) return -1; else return 0; } int DP() { int ret = -0x7fffffff; for (int i = 1; i <= N; ++i) { // 枚举齐王拿多少匹强马出来参赛 dp[0][i] = dp[0][i-1] + judge(i, N-i+1); // 预处理所有的边界情况 dp[i][i] = dp[i-1][i-1] + judge(N-i+1, N-i+1); for (int j = 1; j < i; ++j) { // 枚举田忌拿多少匹弱马出来比赛, 这里边界就不要再计算了 // 这样一确定范围, 那么保证至少有一匹强马, 一匹弱马 dp[j][i] = max(dp[j-1][i-1] + judge(N-j+1, N-i+1), dp[j][i-1] + judge(i-j, N-i+1)); } } for (int i = 0; i <= N; ++i) { ret = max(ret, dp[i][N]); } return ret; } int main() { while (scanf("%d", &N), N) { for (int i = 1; i <= N; ++i) { scanf("%d", h1+i); } for (int i = 1; i <= N; ++i) { scanf("%d", h2+i); } sort(h1+1, h1+1+N); sort(h2+1, h2+1+N); printf("%d\n", 200*DP()); } return 0; }
纯贪心策略比上面简单的安排齐王最强的马的对手要考虑的多一些. 每次当齐王的最强的马与田忌最强的马相等时, 再考虑齐王最弱的马的一个贪心策略, 如果齐王最弱的马比田忌最弱的马还要弱的话, 那么这匹马迟早要输, 选择输给田忌最弱的马. 然后暂时搁置最强马的计较.再次比较双方各自最弱的马. 如果遇到齐王最弱马强于田忌, 那么田忌的最弱马就一定要输, 那么选择输给齐王的最强的马. 难点就是最强和最弱都相等. 这个时候已经没有直观的最优方案. 详细分析如下:
At, a1, a2, a3, ... ai, Bt // 都是从小到大拍好序, At = Aq, Bt = Bq
Aq, b1, b2, b3, ... bi, Bq
这个时候我们要假设a1, a2... 中没有与At, Bt相等的元素; 同样b1, b2, ... 中都没有与Aq, Bq相等的元素. 因为我们考假设A,B与对方的内部元素发生关系, 而相等的元素可视作A,B彼此发生关系.
先讨论At, Aq如何处理, 如果At与Aq拼了, 那么收益为0, 如果At与bj比, 那么田忌一定输一场, Aq就一定和ak比, 田忌一定赢一场, 收益为0.
在讨论Bt, Bq如何处理, 如果Bt与Bq拼了, 那么收益为0, 如果Bt与bj比, 那么田忌一定赢一场, Bq就一定和ak比, 田忌一定输一场, 收益为0.
因此我们可以发现, 无论如何安排, 就单单考虑这四匹马的结果, 收益都一定为0. 那么是不是任意安排结构都一眼呢? 答案No. 因为如果我们如果选择输一场, 那么田忌的马再次与齐王对齐会发现田忌的马向后平移了一个位置, 虽然最后结果一样, 但是为其他马创造了更好的机会. 而这其中最优的就是拿At与Bq比, 虽然输了, 但是通过前面的论证我们可以知道这四匹马的综合考虑结果收益一定为0. 此后的序列就是:
a1, a2, a3, ... ai, Bt
Aq, b1, b2,... bi-1, bi
这样的局面肯定对田忌有利. 分析完毕.
#include <cstring> #include <cstdio> #include <cstdlib> #include <algorithm> #include <iostream> using namespace std; /* 题意:田忌和齐王各有N匹马, 现在要我们去帮助田忌和齐王进行比试, 赢一次获得200白银 平局0白银, 输了的话亏200白银, 问田忌最多能够过得多少白银. 解法:贪心规则, 有一种是将齐王和田忌的马都作一个排序, 然后均从大到小扫描, 如果田忌 能赢的话就那最大的马赢齐王最大的马, 如果输的话, 就拿最小的马输给齐王. 因为最大 的的马如果大于齐王的最大的马, 那么这匹马无论如何肯定会赢一次, 那么赢对方实力 最强的马肯定是最优的, 如果没有马能够和对方的马匹敌的话, 那么一定会输一次, 那么 拿最次的马去输也一定是最优的. 这种贪心规则难就难在不好处理最大的马实力相等的 情况: 比如 1 2 3 对阵 1 2 3时, 如果相等的时候打平, 那么收益为0. 如果拿最小的马 拼了, 收益为200. 又有 2 3 对阵 1 3 如果拿最小的马去输, 那么收益为0. 否则收益 为200. 所以对于相等的情况的考虑是很复杂的. 因此上述的贪心规则还是存在问题. 一种的方法处理上面最快的马打平的方法是再把田忌的最慢的马与齐王最慢的马作一个 比较, 如果能赢, 那么则赢之, 再比较互相最慢的, 如果相等的话, 就用最慢的拼掉 齐王最快的. */ int N, h1[1005], h2[1005]; int main() { int win; while (scanf("%d", &N), N) { win = 0; for (int i = 1; i <= N; ++i) { scanf("%d", h1+i); } for (int i = 1; i <= N; ++i) { scanf("%d", h2+i); } sort(h1+1, h1+1+N); sort(h2+1, h2+1+N); int p = 1, q = N, m = 1, n = q; while (p <= q) { if (h1[q] > h2[n]) { // 如果田忌一定能赢, 最大马要赢的有价值 ++win, --q, --n; } else if (h1[q] < h2[n]) { // 如果田忌一定会输, 则用最小的马输 --win, ++p, --n; } else { if (h1[p] > h2[m]) { // 如果齐王最慢的马一定输, 那么输给对方最慢的 ++win, ++p, ++m; } else { win -= h1[p] < h2[n]; ++p, --n; } } } printf("%d\n", 200*win); } return 0; }