广西 2017 邀请赛 Duizi and Shunzi

2 篇文章 0 订阅

题目描述

Nike likes playing cards and makes a problem of it.
Now give you n integers,ai(1 ≤ i ≤ n)
We define two identical numbers (eg: 2,2) a Duizi, and three consecutive positive integers (eg: 2,3,4) a Shunzi.
Now you want to use these integers to form Shunzi and Duizi as many as possible.
Let s be the total number of the Shunzi and the Duizi you formed.
Try to calculate max(s).
For example, {1,2,3,4} can form Shunzi ({1,2,3}), in this case s= 1. {1,2,3,1,2,3,1,2,3} can form 3 Shunzi ( {1,2,3}, {1,2,3}, {1,2,3} ), in which case s=3, but it can also form 3 Duizi ({1,1},{2,2},{3,3}) and 1 Shunzi ({1,2,3}), and s=4 in this case. It is obvious that max(s) is 4. So the answer is 4.
Each number can be used only once.

输入

The input contains several test cases.
For each test case, the first line contains one integer n(1 ≤ n ≤ 106). Then the next line contains n space-separated integers ai(1 ≤ ai ≤ n).

输出

For each test case, output the answer in a line.

样例输入

7
1 2 3 4 5 6 7
9
1 1 1 2 2 2 3 3 3
6
2 2 3 3 3 3
6
1 2 3 3 4 5

样例输出

2
4
3
2

提示

Case 1: (1, 2, 3), (4, 5, 6)
Case 2: (1, 2, 3), (1, 1), (2, 2), (3, 3)
Case 3: (2, 2), (3, 3), (3, 3)
Case 4: (1, 2, 3), (3, 4, 5)

题意

给一个序列,让你从中找三个连续的数组成一个顺子,或者找两个相同的组成一个对子,看最多能找出几个。

思路

贪心+动态规划。
首先,对子是两个,顺子是三个,肯定取对子的优先级高于顺子,三个三个的看,每三个数(三个数最对那一个顺子。因为如果拿两个顺子也就是说可以拿三个对子,显然拿对子更优。),优先取顺子,但是往外拿牌的时候要优先拿顺子,比如1,2,3,3,4…。这个序列拿的时候要优先拿1,2,3这个顺子。因为剩下一个三可能会对后续的结果有贡献,产生影响,但是1却不会。所以再开一个数组,存当前状态。即我代码中的b数组。
所以这道题目一个核心就是,数的时候,优先数对子,拿的时候优先拿顺子。

另一个核心是我的队友想出来的。就是动态规划的那部分。
每更新到一个值,取这个值的前三个能得到的最大的贡献加上这三个之前能得到的贡献,和,单独把这一个数全部拿成对子加上这个数之前能得到的贡献的和的最大值。
dp[k]的含义就是从1到k这些数能得到的最大贡献。
dp状态转移方程见代码。

代码

#include <iostream>
#include <math.h>
#include <algorithm>
#include <map>
#include <queue>
#include <string.h>
#include <stdio.h>
#include <fstream>
#define mx 1000007
#define mem(a) memset(a,0,sizeof(a))
#define mem2(a) memset(a,-1,sizeof(a))
using namespace std;

int b[mx];
bool f;
int fun (int x,int y,int z,int i)
{
    int ans=0;
    ans+=x/2+y/2+z/2;

    if(b[i]&&b[i-1]&&b[i-2])
       f=true;
    if(b[i]%2&&b[i-1]%2&&b[i-2]%2)
       ans++;
    return ans;
}

int judge (int x,int y,int z)
{
    if(x%2&&y%2&&z%2)
        return 1;
    else
        return 0;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("a.txt","r",stdin);
#endif // ONLINE_JUDGE
    ios_base::sync_with_stdio(false);
    cin.tie(0);

    int n,a[mx],x,ans[mx],y;
    while(cin>>n)
    {
        mem(a);
        mem(b);
        mem(ans);
        for(int i=0; i<n; ++i)
            cin>>x,a[x]++,b[x]++;
        ans[1]=a[1]/2,ans[2]=a[2]/2+ans[1];
        b[1]=a[1]%2,b[2]=a[2]%2;
        for(int i=3; i<=n; ++i)
        {
            f=false;

            x=ans[i-3]+fun(a[i],a[i-1],a[i-2],i);
            y=ans[i-1]+a[i]/2+judge(b[i],b[i-1],b[i-2]);

            if(x>y)
            {
                if(f)
                    b[i]--,b[i-1]--,b[i-2]--;
                b[i]%=2,b[i-1]%=2,b[i-2]%=2;
                ans[i]=x;
            }
            else
            {
                if(f)
                    b[i]--,b[i-1]--,b[i-2]--;
                b[i]%=2,b[i-1]%=2,b[i-2]%=2;
                ans[i]=y;
            }
        }
        cout<<ans[n]<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

渴鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值