2017-2018 ACM-ICPC Asia East Continent League Final (ECL-Final 2017) J - Straight Master (思维乱搞)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/83749113

J. Straight Master

time limit per test

2.0 s

memory limit per test

256 MB

input

standard input

output

standard output

A straight is a poker hand containing five cards of sequential rank, not necessarily to be the same suit. For example, a hand containing 7 club, 6 spade, 5 spade, 4 heart and 3 diamond forms a straight. In this problem, we extend the definition of a straight to allow 3 to 5 cards of sequential rank. Hence a hand containing K spade, Q club, and J heart is also a straight.

Mr. Panda is playing a poker game called Straight Master. The game uses a large deck of card that has N ranks from 1 to N. The rule of the game is simple: split the cards in Mr. Panda's hand into several straights of length from 3 to 5.

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

Now given a hand of cards, can you help Mr. Panda to determine if it is possible to split the cards into straights?

Input

The first line of the input gives the number of test cases, T. T test cases follow.

Each test case contains two lines. The first line contains an integer N, indicating the number of ranks in the deck. The next line contains N integers a1, a2, ..., aN indicating the number of cards for each rank in Mr. Panda's hand.

  • 1 ≤ T ≤ 100.
  • 1 ≤ N ≤ 2 × 105.
  • 0 ≤ ai ≤ 109.
  • .

Output

For each test case, output one line containing "Case #x: y", where x is the test case number (starting from 1) and y is Yes if Mr. Panda can split all his cards into straights of length from 3 to 5, or No otherwise.

Example

Input

Copy

2
13
1 2 2 1 0 0 0 0 0 0 0 0 0
13
1 1 1 1 0 1 1 0 0 0 0 0 0

Output

Copy

Case #1: Yes
Case #2: No

Note

In the first test case, Mr. Panda can split his cards into two straights: [1, 2, 3] and [2, 3, 4]. In the second test case, there is no way to form a straight for card 6 and 7.

题意:

有n种牌,给你每张牌的数量a[i],牌的大小就是i。如果有连续的3/4/5张牌,就可以组成顺子(i,i+1,i+2/i,i+1,i+2,i+3/i,i+1,i+2,i+3,i+4)。

问你能不能把这些牌分成一堆堆的顺子,不能有单个的牌。

解析:

先贴一下大佬的题解和代码

相当于每次把一个长度为3~5的区间整体减1,问最后是否能够全部减成0。

显然,每次把一个长度大于5的区间整体减1也是可以的,因为6 = 3+3,7 = 3+4......

所以问题就变成了每次修改一个长度大于等于3的区间。

可以先维护原本序列的差分,然后区间整体减1就相当于a[l]--, a[r+1]++。

所以只要贪心的枚举每个大于0的位置,然后找后面的离他最近的小于0的数字匹配,把前者减,后者加,就可以了。

如果最近的距离 < 3那么就是不可以。

构造成差分一个很棒的性质就是我们模拟区间更新的时候,就不需要把[l,r]这一段值都更新。

因为你更新一个差分数组,该区间内两两之间的差值是不会变的,变的只是边界上的差值,所以只需要更新a[l],a[r]就可以了

然后代码里面找一个最近的负值匹配,这样能保证c[i]就是区间[i,r)的最小值,即是区间更新的值的大小。

然后匹配完成之后如果c[r]>0,说明还可以继续匹配,那么我们就贪心地继续匹配下去。如果不继续下去,可能下一次

的匹配就无法完成。用一个例子来讲

8

a[]:

3    6    7    8    7    6    5

c[]:

3    3    1    1   -1   -1   -1   -5

3    3    3    3    2    1             //第一次更新[1,4]-3,然后c[5]变成2>0,继续贪心匹配,更新到[5,5]-2,c[6]=1>0,更新[6,6]-1,end

      3    3    3    3    3    3           //[2,8]-3 

            1    1    1    1    1        //[3,8]-1

                  1    1    1    1        //[4,8]-1

如果第一次就到[1,4]为止,那么c[5]=2,那么就无法更新[5,5],[6,6]这两个区间但是其实这两个区间完全是可以跟在[1,4]后面更新

掉的,因为他们接在一起是连续的,并且值是递减的。

虽然讲了那么多,我自己也还不是完全理解大佬的想法...为什么要用差分..为什么要用正负值匹配....

原文

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>

using namespace std;
typedef long long LL;
const int maxn = 200000 + 100;

int a[maxn];
LL c[maxn];

int main()
{
    int t;
    scanf("%d", &t);
    for (int ca = 1; ca <= t; ca++)
    {
        memset(a, 0, sizeof(a));
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) scanf("%d", &a[i]);
        for (int i = 1; i <= n+1; i++) c[i] = a[i]-a[i-1];

        bool flag = true;
        int r = 0;
        for (int i = 1; i <= n; i++)
        {
            while(c[i] > 0)
            {
                while(c[r] >= 0)
                    if (++r > n+1) flag = false;
                if (r-i <= 2) flag = false;
                if (!flag) break;
                c[r] += c[i], c[i] = 0;
                if (c[r] > 0) c[i] = c[r], c[r] = 0;
            }
            if (!flag) break;
        }

        printf("Case #%d: %s\n", ca, flag?"Yes":"No");
    }
}

下面讲讲我自己的想法,因为3,4,5是可以组成任意长度的。

那么我们对于每遍历到一张牌,都先把他往前面的牌去匹配。如果前面的牌不够了,在往后面找2张牌,来组成顺子

譬如

3    6    7    8    7     7

3    3    3    3   2     2          //当遍历到8时,我们先让他匹配前面的顺子,然后多出来2,那么我们找后面2张牌,组成新的顺子

      3    3    3   3     3          //7也一样,先匹配前面的,发现匹配完

            1    1   1     

                  2    2    2

每一次与前面的组成顺子/组成新的顺子就要更新ex4[]数组,用来表示ex4[i]表示末尾到i的顺子的牌的数量,

即i+1可以匹配前面的顺子的数量

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int MAX = 2e5+100;

int a[MAX];
int ex4[MAX],ex5[MAX];

int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        cas++;
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]),ex4[i]=ex5[i]=0;
        int flag=1;
        for(int i=1;i<=n;i++)
        {
            if(!a[i]) continue;
            if(ex4[i-1]>a[i])
            {
                ex4[i]+=a[i];
                ex4[i-1]=0;
                a[i]=0;
            }
            else
            {
                a[i]-=ex4[i-1];
                ex4[i]+=ex4[i-1];
                ex4[i-1]=0;
                if(a[i])
                {
                    int ok=0;
                    for(int j=i+1;j<=i+2&&j<=n;j++)
                    {
                        if(a[j]>=a[i])
                        {
                            ok++;
                        }
                    }
                    if(ok<2)
                    {
                        flag=0;
                        break;
                    }
                    else
                    {
                        a[i+1]-=a[i];
                        a[i+2]-=a[i];
                        ex4[i+2]+=a[i];
                        a[i]=0;
                    }
                }
            }

        }
        
        printf("Case #%d: ",cas);
        if(flag) printf("Yes\n");
        else printf("No\n");

    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/83749113