• 蓝桥杯---波动数列(dp)(背包)(待解决)


    问题描述
      观察这个数列:
      1 3 0 2 -1 1 -2 ...

      这个数列中后一项总是比前一项增加2或者减少3。

      栋栋对这种数列很好奇,他想知道长度为 n 和为 s 而且后一项总是比前一项增加a或者减少b的整数数列可能有多少种呢?
    输入格式
      输入的第一行包含四个整数 n s a b,含义如前面说述。
    输出格式
      输出一行,包含一个整数,表示满足条件的方案数。由于这个数很大,请输出方案数除以100000007的余数。
    样例输入
    4 10 2 3
    样例输出
    2
    样例说明
      这两个数列分别是2 4 1 3和7 4 1 -2。
    数据规模和约定
      对于10%的数据,1<=n<=5,0<=s<=5,1<=a,b<=5;
      对于30%的数据,1<=n<=30,0<=s<=30,1<=a,b<=30;
      对于50%的数据,1<=n<=50,0<=s<=50,1<=a,b<=50;
      对于70%的数据,1<=n<=100,0<=s<=500,1<=a, b<=50;
      对于100%的数据,1<=n<=1000,-1,000,000,000<=s<=1,000,000,000,1<=a, b<=1,000,000。

    开始的时候就感觉这道题应该没有那么简单,但是还是抱着试试看的心情做了一下,结果。。。就没有结果了,果断20%,剩下的全是TL,即使是之后的剪枝也是没什么作用(其实这也正常,粗略的算一下也会知道会超时的)
    #include <iostream>
    #include <cstdio>
    using namespace std;
    int count=0,n,result,a,b,sum=0;
    bool  sub(int i,int ceng){
    	int ss=(2*i+(ceng-1)*-b)*ceng/2;
    	return sum+ss<=result;
    }
    bool  add(int i,int ceng){
    	int ss=(2*i+(ceng-1)*a)*ceng/2;
    	return sum+ss>=result ;
    }
    void dfs(int i,int cou){
    	
    	sum+=i;
    	int temp=count;
    	
    	if(sum==result&&cou==n) { count++; 	} //cout<<i<<' ';
    	if(cou>=n){ sum-=i;   return ;} 
    	
        if(sub(i-b,n-cou))//如果剩下的全部为减法都不能成立的话就直接不用继续深搜下去了
    	                   //但是这样的优化好像是杯水车薪,依然超时 
    	  dfs(i+a,cou+1);
    	//if(count!=temp) cout<<i<<' ';
        //	temp=count;
     	if(add(i+a,n-cou))//与上同理 
    	  dfs(i-b,cou+1);
       //if(count!=temp) cout<<i<<' ';	
    	sum-=i;
    	return;
    }
    int main(){
     scanf("%d%d%d%d",&n,&result,&a,&b);
     int top=(result*2/n-(n-1)*-b)/2, bott=(result*2/n-(n-1)*a)/2;
     cout<<top<<' '<<bott<<endl;
     for(int i=bott; i<=top; i++){
       // cout<<"底数为"<<i<<"      "<<sum<<endl;
      	dfs(i,1);
       // cout<<endl; 
     }
     cout<<count<<aaa<<endl;
     return 0;
    }



    最后才知道是动态的一道题,不过好像还是有点不太懂,之后整理吧。。。


    找到了下面的一个代码:(找了好长时间才找到这么一个100%AC的,但是好像还是有点不太懂)
    http://blog.csdn.net/quzhongrensan511/article/details/23156363

    原题可化为nx+(n-1)p(1)+(n-2)p(2)+…+p(n)=s,其中n为数列长度,x为初值,p(i)={a,-b}。本题的目标是给出不同的p序列,使得等式成立并且x为整数。自然而然可以想到枚举的方法,给出不同的序列,令t=s-Σi*p(n-i),若t%n==0则是一种可取的方案。直接枚举肯定会超时,所以需要进一步考虑。注意到,a和b的总数为n(n-1)/2个(所有p前系数的和),所以我们只需要枚举a的个数,将t修改为t=s-ca-(n(n-1)/2-c)b,c为枚举数,0<=c<=n(n-1)/2。

    当然满足条件的c的个数并不是我们想要的,因为给定一个c,存在多种组合方式。但是可以发现,每个c都是由1~n-1中若干元素组成的。于是问题转化为求容量为c的01背包的方案数。对于本题,可以写为:(f[i][j]为前i个物体构成j体积的方案数,第i个物体的体积为i)

    f[i][j]=f[i-1][j],  i>j

             f[i-1][j]+f[i-1][j-i],  j>=i

    注意到递推式只和前一状态有关,故可以使用滚动数组;根据定义,可以预先算出每一个f[n][i],避免重复计算;前i个货物最多只能达到i*(i+1)/2的体积,所以大于这个数值的部分没有必要计算。具体的请看代码,其实就这么几行。

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #define MAXN 1100
    #define MOD 100000007
    using namespace std;
    
    int F[2][MAXN*MAXN];
    int e = 0;
    long long n,s,a,b;
    int cnt = 0;
    
    void calc(int elem)
    {
        int i,j;
        memset(F,0,sizeof(F));
        F[e][0]=1;
        for(i=1;i<n;i++)
        {
            e=1-e;
            for(j=0;j<=i*(i+1)/2;j++)
            {
                if(i>j)
                    F[e][j]=F[1-e][j];
                else
                    F[e][j]=(F[1-e][j]+F[1-e][j-i])%MOD;
            }
        }
    }
    
    int main()
    {
        scanf("%I64d%I64d%I64d%I64d",&n,&s,&a,&b);
        long long i,t;
        calc(n*(n-1)/2);
        for(i=0; i<=n*(n-1)/2; i++)
        {
            t = s - i*a + (n*(n-1)/2-i)*b;
            if(t%n==0)
               cnt = (cnt+F[e][i])%MOD;
        }
        printf("%d",cnt);
        return 0;
    }
    


  • 相关阅读:
    「日常训练」Single-use Stones (CFR476D2D)
    「日常训练」Greedy Arkady (CFR476D2C)
    「Haskell 学习」二 类型和函数(上)
    「学习记录」《数值分析》第二章计算实习题(Python语言)
    「日常训练」Alena And The Heater (CFR466D2D)
    Dubbo 消费者
    Dubbo 暴露服务
    Rpc
    git fail to push some refs....
    Spring Cloud (6)config 客户端配置 与GitHub通信
  • 原文地址:https://www.cnblogs.com/zswbky/p/5431964.html
Copyright © 2020-2023  润新知