连续邮资问题(DFS搜索回溯)

题目内容:

在这里插入图片描述

思路简述:

首先既然是n个数字,可重复选取m个,那么枚举长度为n的序列a的所有可能情况,限制条件为:
1.a数组严格单调递增
2.a数组第一个数字为1
然后如果这样枚举那么枚举空间是无限的,因为此时:a[0] = 1 , a[1]的枚举范围可以是[2,+∞)。
所以需要再加一些限制:
假设a[0]a[k]选m个能组成的最大连续数字为:1s,那么a[k+1]的枚举范围一定是:[a[k]+1,s+1];
因为如果a[k+1] > s+1,那么a[k+1]的产生并不会对结果有提升。

在确定了有限的枚举范围之后,问题就变成了在有限范围内枚举,只需要dfs即可,这里每次枚举完成之后,
对于长度为n的数组,要确定它能组合成的最长连续值为多少,这个问题需要再次枚举(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/91359790