dfs+dp
dfs枚举每种情况,每层递归确定第k个数i:i = a[k-1]+1 to a[k-1]*n+1
当枚举完一个序列时,使用check()测试它能达到的max
使用dp。设dp[i]为凑成面值为i的最少张数
for(int k=1;a[k]<=i&&k<=t;k++){ if(dp[i-a[k]]<n){ dp[i]=min(dp[i], dp[i-a[k]]+1); } }
注意dp[i]一开始要设成最大值
代码如下:
#include<iostream> #include<cstring> #define Size 41 using namespace std; int a[Size]; int dp[10005]; int best[Size]; int n,t;//n张 t种 int num=0;void save(int x){ num=x; for(int i=1;i<=t;i++){ best[i]=a[i]; } } int check(){ //memset(dp,0x3f,sizeof(dp)); dp[0]=0; int ans=0; bool ok=true; //for(int i=1;i<=t;i++)cout<<a[i]<<" "; //cout<<endl; for(int i=1;ok;i++){ ok=false; dp[i]=0x3f3f3f3f; for(int k=1;a[k]<=i&&k<=t;k++){ if(dp[i-a[k]]<n){ dp[i]=min(dp[i], dp[i-a[k]]+1); ok=true; } } if(ok)ans=i; //cout<<dp[i]<<‘ ‘; } //cout<<endl<<endl; return ans; } void dfs(int k){if(k>t){ int x=check(); if(x>num)save(x); return; } for(int i=a[k-1]+1;i<=n*a[k-1]+1;i++){ a[k]=i; dfs(k+1); } } int main(){ cin>>n>>t; a[1]=1; num=1; dfs(2); for(int i=1;i<=t;i++){ cout<<best[i]<<‘ ‘; } cout<<endl<<"MAX="<<num<<endl; cout<<f; return 0; }
By the way:a[1]一定是1!
PS:你可以看到我把check的第一行memset注释掉了,因为大部分时候很长的dp数组后面的很多元素都没有用(max没有那么大),所以只需要在循环里用到一个写一个dp[i]=x03f3f3f3f就可以了。memset使程序变得很慢很慢。(真的很慢啊...)(你真的不需要再试它有多慢了,因为我已经花一个小时帮你试了无数遍了...)
如果想继续优化,可以边dfs边维护dp和当前max2的值,这样dfs循环就可以写成这样:
i = a[k-1]+1 to max2+1
如果i>max2+1,那么max2+1这个数将始终无法凑成,结果不会更优
时间: 2024-10-12 11:37:49