analysis

9 篇文章 0 订阅
9 篇文章 0 订阅

analysis

Time Limits: 1000 ms Memory Limits: 128000 KB

Description
给你N个数字,将其分成若干子序列,每个序列都必须是递增或递减的.
希望子序列的数量越少越好。

Input
本题有多组数据
对于每组数据,第一行一个整数N
第二行N个整数Ai.

Output
对于每组数据,输出最少的个数

Sample Input

1
5
3
1 3 2

Sample Output

1
2

Hint
数据范围
N<=50
保证Ai中的数字不重复,且0<=Ai<=200
每个输入文件中不超过10组数据
提示
对于Pascal选手,推荐使用seekeof判断文件结束

》》题目感知:题目太码农,解法太玄学!!

总而言之,题目出得有点过于坑


解题思路

主要算法就是搜索,直接暴力,枚举(或二分)答案,然后再深搜迭代加深。

设tot1表示为降序列的个数,tot2表示为升序列的个数,Ui表示为第i个生序列的队首,Di表示为第i个降序列的队首。

有如下几种操作:

  1. 若tot2=0,新开一个U存入当前的数,深搜一遍;
  2. 若tot1=0,新开一个D存入当前的数,深搜一遍;
  3. 若tot1>0且存在Di>当前的数,让当前的数存入符合条件的Di中最小的序列,深搜一遍;
  4. 若tot2>0且存在Ui<当前的数,让当前的数存入符合条件的Ui中最大的序列,深搜一遍;
  5. 若tot1>0但不存在Di>当前的数,新开一个D存入当前的数,深搜一遍;
  6. 若tot2>0但不存在Ui<当前的数,新开一个U存入当前的数,深搜一遍。

注意,一定要按照这种顺序(除非你打得十分优美),否则会TLE,至于为什么,我也不知道。同时,注意一下输入,非常坑。这就是我为什么说“》》题目感知:题目太码农,解法太玄学!!”的原因了。

Codes:

#include<cstdio>
#include<cstring>
#include<cmath>
#define fo(i,x,y) for(int i=x;i<=y;i++)
using namespace std;

int n,a[51],mid,tot1,tot2,U[51],D[51];
char num;
bool q;

void can(int);

int main()
{
    for(;;)//相当于while(1)
    {

        num=getchar();
        if(num<'0' || num>'9')break;
        int x=num-'0';
        for(;;)
        {
            num=getchar();
            if(num<'0' || num>'9')break;
            x+=x*9+(num-'0');
        }
        fo(i,1,x)scanf("%d",&a[i]);
        scanf("\n");
        n=x;
        int l=1,r=floor(sqrt(n)),ans=r;/*枚举到√n即可,易证*/
        while(l<=r) //二分
        {
            mid=(l+r)>>1;
            q=0;
            tot1=tot2=0;
            can(1);
            if(q)
            {
                r=mid-1;
                ans=mid;
            }else{
                l=mid+1;
            }
        }
        printf("%d\n",ans);
    }   
}

void can(int w)/*搜索顺序很关键*/
{
    bool p=0,o=0;
    int wz,s,z;
    if(tot1+tot2>mid)return;
    if(w>n){q=1;return;}
    if(tot2==0)
    {
        U[++tot2]=a[w];
        can(w+1);if(q)return;
        tot2--;/*这里一定不要丢掉*/
    }
    if(tot1==0)
    {
        D[++tot1]=a[w];
        can(w+1);if(q)return;
        tot1--;/*这里一定不要丢掉*/
    }
    if(tot1>0)
    {
        z=0x7fffffff;
        for(int i=1;i<=tot1;i++)
        {
            if(D[i]>a[w] && D[i]<z)
            {
                z=D[i];wz=i;
            }
        }
        if(z<0x7fffffff)
        {
            s=D[wz];
            D[wz]=a[w];
            can(w+1);if(q)return;
            D[wz]=s;/*这里一定不要丢掉*/
        }else p=1;
    }
    if(tot2>0)
    {
        z=-1;
        for(int i=1;i<=tot2;i++)
        {
            if(a[w]>U[i] && U[i]>z)
            {
                z=U[i];wz=i;
            }
        }
        if(z>-1)
        {
            s=U[wz];
            U[wz]=a[w];
            can(w+1);if(q)return;
            U[wz]=s;/*这里一定不要丢掉*/
        }else o=1;
    }
    if(p)
    {
        D[++tot1]=a[w];
        can(w+1);if(q)return;
        tot1--; /*这里一定不要丢掉*/
    }
    if(o)
    {
        U[++tot2]=a[w];
        can(w+1);if(q)return;
        tot2--;/*这里一定不要丢掉*/
    }
}

这些“/这里一定不要丢掉/”,重要的事说三遍,这里说六遍,脑补一下

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值