算法设计与分析:连续邮资问题(搜索)实验报告

实验内容:

在这里插入图片描述

算法讲解:

首先既然是 n n 个数字,可重复选取 m m 个,那么枚举长度为 n n 的序列 A A 的所有可能情况,分别计算出每种序列“ m m 个(可重复)能组成的最长连续邮资为多大”(后面将其称为**“序列的最大值”**),找出那个“序列的最大值”最大的序列就是答案了。
但是我们发现因为自然数是无限大的, A A 序列的每个位置理论上都可以有无数种可能,所以我们要通过提前分析,得到一些 A A 序列的限制条件,以减少我们的枚举量。

限制一 A A 数组严格单调递增(我们将从小到大排序后相同的序列看成一个序列)
限制二 A A 数组第一个数字为 1 1 (否则不可能组成数字 1 1

但是如果这样枚举那么枚举空间是无限的,因为此时: A 1 = 1 , a 2 A_1 = 1 , a_2 的枚举范围可以是 [ 2 , + ) [2,+∞) 。所以需要再加一些限制:

限制三:假设 A 1 A_1 A k A_k m m 个能组成的连续数字为: 1 1 S S ,那么 A k + 1 A_{k+1} 的枚举范围一定是: [ A k + 1 , S + 1 ] [A_k+1,S+1] ;因为如果 A k + 1 > S + 1 A_{k+1} \gt S+1 ,那么 A k + 1 A _ {k + 1} 的产生并不会对结果有提升(因为 A A 序列依旧没办法组成数值: S + 1 S+1 )。在确定了有限的枚举范围之后,问题就变成了在有限范围内枚举,只需要 d f s dfs 即可,这里每次枚举完成之后,对于长度为 n n 的序列 A A ,要确定它能组合成的最长连续值为多少,这个问题需要再次暴力枚举( d f s 2 dfs2 函数)。

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 1000052 ;
vector < int > a ; // 当前枚举序列
vector < int > seq ; // 答案序列
bool vis[maxn] ;

void dfs2( int now , int dep , int res )
{
    if( now<maxn ) vis[now] = true ;
    if( dep >=a.size() ) return ;
    for( int i=0; i<=res; i ++) {
        dfs2( now+a[dep]*i , dep+1  , res-i );
    }
}
int Get_max_ans( int n , int m ) // 计算a数组的前n个数字,选m个数字(可重复选取)能得到的最大连续值
{
    int ret = 1 ;
    memset( vis , 0, sizeof(vis) ) ;
    dfs2( 0 , 0 , m ) ; // 枚举前n个数字选m的个所有选法
    while( vis[ret] == true ) ret++ ;
    return ret-1;
}
void dfs( int dep , int max_ans , int& ret , int n , int m ) { // 枚举所有可能的a序列
    if( dep == n ){ // 枚举到 n 回溯
        if( ret < max_ans ) { // 将当前最优序列存储到seq中
            ret = max_ans ;
            for( int i=0; i<n; i++ ){
                seq[i] = a[i] ;
            }
        }
        return ;
    }
    int st = (dep==0) ? 1 : (a[dep-1]+1) ; // a[dep]枚举范围为: [ a[dep-1] , max_ans+1 ]
    for( a[dep]=st; a[dep]<=max_ans+1; a[dep]++ ) {
        int temp = Get_max_ans( dep , m ) ;
        dfs( dep+1 , temp , ret , n , m ) ;
    }
}
int Work( int n , int m )
{
    int ret = 0 ;
    a.clear();  seq.clear();
    for( int i=0; i<n; i++ ) {
        a.push_back(0);
        seq.push_back(0);
    }
    dfs( 0 , 0 , ret , n , m ) ;
    return ret;
}
void Test_time_table() // 输出时间表
{
    printf("\tn\tm\tans\ttime\n");
    for( int n=1; n<=6; n++ ) {
        for( int m=1; m<=6; m++ ) {
            double t_star = clock() ;
            int ans = Work( n , m ) ;
            double t_end = clock() ;
            printf("\t%d\t%d\t%d\t%.2f\n",n,m,ans,t_end-t_star);
        }
    }
}
int main()
{
    //Test_time_table(); //用于测试算法效率
    int n, m;
    scanf( "%d%d", &n, &m ) ;
    double t_star = clock() ; // 计时起点
    int ans = Work( n , m ) ; // 计算答案
    double t_end = clock() ; // 计时终点
    /// 输出答案
    for( int i=0; i<seq.size(); i++ ) {
        printf( "%d ", seq[i] ) ;
    }
    printf( "max_val = %d \n", ans );
    printf( "using time : %.2f ms\n", t_end-t_star ) ;
    return 0;

运行结果截图:

运行一:

在这里插入图片描述

运行二:

在这里插入图片描述

发布了227 篇原创文章 · 获赞 142 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/qq_36306833/article/details/93199533