实验内容:
算法讲解:
首先既然是 n个数字,可重复选取 m个,那么枚举长度为 n的序列 A的所有可能情况,分别计算出每种序列“选 m个(可重复)能组成的最长连续邮资为多大”(后面将其称为**“序列的最大值”**),找出那个“序列的最大值”最大的序列就是答案了。
但是我们发现因为自然数是无限大的, A序列的每个位置理论上都可以有无数种可能,所以我们要通过提前分析,得到一些 A序列的限制条件,以减少我们的枚举量。
限制一: A 数组严格单调递增(我们将从小到大排序后相同的序列看成一个序列)
限制二: A 数组第一个数字为 1(否则不可能组成数字 1)
但是如果这样枚举那么枚举空间是无限的,因为此时: A1=1,a2的枚举范围可以是 [2,+∞)。所以需要再加一些限制:
限制三:假设 A1 到 Ak 选 m 个能组成的连续数字为: 1到 S,那么 Ak+1 的枚举范围一定是: [Ak+1,S+1];因为如果 Ak+1>S+1,那么 Ak+1 的产生并不会对结果有提升(因为 A序列依旧没办法组成数值: S+1 )。在确定了有限的枚举范围之后,问题就变成了在有限范围内枚举,只需要 dfs即可,这里每次枚举完成之后,对于长度为 n 的序列 A ,要确定它能组合成的最长连续值为多少,这个问题需要再次暴力枚举( 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;