zoukankan      html  css  js  c++  java
  • 洛谷 P2687 [USACO4.3]逢低吸纳Buy Low, Buy Lower/ACWing 314 低买 题解

    洛谷 P2687 [USACO4.3]逢低吸纳Buy Low, Buy Lower/ACWing 314 低买 题解

    2019-12-26 xiaoh

    题意

    给定一个有n个数的序列a(1<=n<=5000,且a中每个数在int范围内),求最长严格下降子序列(我们不妨称其为LDS,Largest Descreasing Subsequence)的长度及数量。注:当两个LDS元素完全相同时,就算元素从不同的位置取出,也算作同一个答案

    题解

    首先,数据很小,n只有5000,很明显长度直接套LIS的板子就行。接下来要考虑如何计算数量,并避免重复计算相同的序列。接下来,我们考虑如何在DP时算出满足要求的LDS的数目。将DP方程f[i]=Max{f[j]+1},j∈[i+1,n]且a[i]>a[j]的情况分为三类:
    1、f[j]+1大于f[i],说明存在更长的序列,则f[i]=f[j]+1,而以i为开头的LDS的长度f[i]的数量tot[i]就等于tot[j];
    2、f[j]+1等于f[i],说明以i为开头的LDS后的第二个数可以有多种情况,那么就都要计入tot中,即tot[i]+=tot[j];
    3、f[j]小于f[i],此时,什么也做不了,就过去吧QAQ
    这样我们就初步捋出了大致的算法。但仍然存在一个问题:即两个LDS的元素完全相同时,就算从不同的位置取出,也只能算一个。那么我们考虑对于一对i和j,若a[i]=a[j],且f[i]=f[j],那么很明显这两者中只需要算一个,因为数相同,长度也相同,说明两个序列一定可以重合,因此在处理完i之后j再跑一边找出符合的j,将其tot[j]设为0即可。时间复杂度O(n^2),刚好能过

    Code

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
    	int ret=0,w=1;
    	char ch=getchar();
    	while(ch<'0'||ch>'9')
    	{
    		if(ch=='-') w=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    	return w*ret;
    }
    int n;
    int a[5010];//原数组
    struct node{//num为LDS的长度,tot为总数
    	int num;
    	long long tot;
    }f[5010];
    int ans1=0;//LDS长度
    long long ans2=0;//LDS数量
    map<int,bool> mp;
    int main()
    {
    	n=read();
    	for(int i=1;i<=n;i++) a[i]=read();
    	for(int i=n;i>=1;i--)
    	{
    		f[i].num=1;
    		f[i].tot=1;
    		for(int j=i+1;j<=n;j++)//计算LDS数量和长度
    		if(a[i]>a[j])
    		{
    			if(f[j].num+1>f[i].num) f[i].num=f[j].num+1,f[i].tot=f[j].tot;
    			else if(f[j].num+1==f[i].num) f[i].tot+=f[j].tot;
    		}
    		for(int j=i+1;j<=n;j++)//找出重复部分并删除
    		if(a[i]==a[j]&&f[i].num==f[j].num) f[j].num=f[j].tot=0;
    	}
    	for(int i=1;i<=n;i++)
    	if(f[i].num>ans1) ans1=f[i].num,ans2=f[i].tot;
    	else if(f[i].num==ans1) ans2+=f[i].tot;
    	printf("%d %lld\n",ans1,ans2);
    	return 0;
    }
    

    后记

    其实本人看到这道题的第一反应是:雾草!这道题似乎可以用map来维护重复部分诶!于是乎,O(nlgnlgn)成功的爆炸了……后来看了题解才发现原来正解那么愚蠢QAQ
    理论上到这里就应该结束了,是的,在ACWing里,以上代码就能轻松的拿到AC的好成绩,但是在luogu中,你却惊喜的发现,第九个点死活也过不去,于是乎,笔者下载了一下第九个点的数据,各位看一下输出:
    200 1606938044258990275541962092341162602522202993782792835301376
    
    奉劝正在数位数的各位,别数了,61位。显然,__int128甚至long long也解决不了这个问题,但是我们又不想写高精,那该怎么办呢?突然间,我们想到了long double,这个东西好像比long long存的还多诶。所以我们将输出的种类改为用long double存(有些题解说/1e17存,实际上是不行的,因为精度丢失,直接存就能过),然后呢……AC!

    Code(洛谷版)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read()
    {
        int ret=0,w=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') w=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
        return w*ret;
    }
    int n;
    int a[5010];
    struct node{
        int num;
        long double tot;
    }f[5010];
    int ans1=0;
    long double ans2=0;
    map<int,bool> mp;
    int main()
    {
        n=read();
        for(int i=1;i<=n;i++) a[i]=read();
        for(int i=n;i>=1;i--)
        {
            f[i].num=1;
            f[i].tot=1;
            for(int j=i+1;j<=n;j++)
            if(a[i]>a[j])
            {
                if(f[j].num+1>f[i].num) f[i].num=f[j].num+1,f[i].tot=f[j].tot;
                else if(f[j].num+1==f[i].num) f[i].tot+=f[j].tot;
            }
            for(int j=i+1;j<=n;j++)
            if(a[i]==a[j]&&f[i].num==f[j].num) f[j].num=f[j].tot=0;
        }
        for(int i=1;i<=n;i++)
        if(f[i].num>ans1) ans1=f[i].num,ans2=f[i].tot;
        else if(f[i].num==ans1) ans2+=f[i].tot;
        printf("%d %.0Lf\n",ans1,ans2);
        return 0;
    }
    
    证据在此↓↓↓

  • 相关阅读:
    springMVC静态资源
    MyBatis Generator
    使用springMVC时的web.xml配置文件
    Semaphore
    spring注解驱动--组件注册
    第1章 初始Docker容器
    docker面试整理
    第5章 运输层
    验证码
    带进度条的上传
  • 原文地址:https://www.cnblogs.com/xiaoh105/p/12104750.html
Copyright © 2011-2022 走看看