题目链接: Duizi and Shunzi
大致题意
给你n张牌, 每张牌都是1~n之中的数字, 你可以选择两张数值一样的牌凑成一个对子, 你也可以选择三张数值连续的牌凑成顺子. 每张牌只可以使用一次.
问: 最多能凑出多少对子和顺子.
解题思路
首先我们需要产生尽可能多的对子和顺子, 那么一个对子需要两张牌, 一个顺子需要三张牌.
因此优先级: 对子 > 顺子
考虑到题目中所有的ai都 <= n, 因此我们可以用桶排序的思路来记录每一个数字出现的情况.
由于优先级的因素, 因此对于一张牌而言, 如果自身能成对子, 则就要成尽可能多的对子. 我们不难想到, 若该牌数量为偶数, 则无剩余, 若为奇数, 则有一张剩余.
那么对于该牌数量为奇数的情况, 他有可能和其余牌组成顺子, 那么我们如何判断该不该组成顺子呢?
这里先给出一种错误的想法, 如果你想着我们贪心, 先把所有能组成对子的组成对子, 然后再考虑是否组成顺子, 是不正确的.
例如样例: 1 2 3 3 4 5 很显然组成两个顺子是最优的, 但是如果按照上述做法, 只能组成一个对子.
在这里我们需要从小到大枚举所有的牌(意味着枚举到i时, i+1与i+2数量均为原先的数量, 也没有自身组成对子), 如果当前x为奇数张 且 x+1, x+2牌均存在的情况下, 我们需要考虑是否需要组成顺子.
这里的考虑方式还是通过优先级. 我们为什么先考虑对子再考虑顺子? 因为对子的代价为2, 顺子的代价为3. 那么如果我们可以降低顺子的代价, 不就可以使得顺子的优先级提升吗?
这里再提供一种错误的想法, 如果你认为, 我当前的x不是奇数张吗? 那么我直接把 x, x + 1, x + 2组成顺子, 这样代价不也是2吗? 这种情况下的顺子和对子的优先级相同, 应该结果是一样的吧? 然后你去敲代码, 发现你又错了!
这里给出一组例子: 1 2 2 3 3 很显然答案是2, 但是你这样做, 答案就是1了.
这里的原因是, 由于我们从小到大枚举, 组成的对子是无后效性的, 而你组成顺子时, 你会对没有遍历到的x+1和x+2的位置产生影响. 因此我们不能单纯的认为这样的顺子代价也是2.
当且仅当x和x+1均为奇数张, 且x+2存在时, 我们组成顺子才是优于对子的. 此时由于x和x+1都是奇数张, 可以认为代价只由x+2产生, 因此代价为1. 相当于我们仅消耗了一张牌, 就使得了结果+1.
AC代码
//头文件
#define rep(i, n) for (int i = 1; i <= (n); ++i)
using namespace std;
typedef long long ll;
const int N = 1E6 + 10;
int a[N];
int main()
{
int n;
while (~scanf("%d", &n)) {
rep(i, n + 2) a[i] = 0;
rep(i, n) {
int x; scanf("%d", &x);
a[x]++;
}
int res = 0;
rep(i, n) {
res += a[i] / 2;
if (a[i] % 2 and a[i + 1] % 2 and a[i + 2]) {
rep(j, 3) a[i + j - 1]--;
res++;
}
}
printf("%d\n", res);
}
return 0;
}