其实这道题就是简单的完全背包问题。从低到高推断出每种硬币的所有面额的解即可。当然,离线的话会慢一点。可以将问题需要求出的子问题全部求出来。下一个问题出现的时候,如果之前已经求解过则不必求解,否则在之前的基础上继续求解。原本觉得没什么好写的,关键是同样的方法用STL中的vector代替数组会超时!搞得以后都不敢用STL了.....
#include <iostream> #include <cstdio> #include <algorithm> #include <vector> #include <cstring> #define MAXN 1000+5 #define MAXK 100+5 #define MAXL 200 using namespace std; char ans[MAXK][MAXN][MAXL]; int vis[MAXK][MAXN]; int L[MAXK][MAXN];//答案的长度记录 void add(int,int); void doit(int,int,int,int); int main() { memset(vis,0,sizeof(vis)); for(int i=0;i<MAXN;i++) {ans[1][i][1]='1';vis[1][i]=1;L[1][i]=1;}//K=1 for(int i=1;i<MAXK;i++) {ans[i][0][1]='1';vis[i][0]=1;L[i][0]=1;}//N=0 int N,K; int LK=1,LN=0;//LK,LN记录上次求解到的位置 while(cin>>N>>K){ if(!vis[K][N]) {doit(LK,LN,K,N);LK=K,LN=N;} for(int i=L[K][N];i>0;i--) cout<<ans[K][N][i]; cout<<endl; } } void doit(int LK,int LN,int K,int N) { for(int i=LK+1;i<=K;i++){ for(int j=1;j<i;j++) { if(vis[i][j]) continue; for(int k=1;k<=L[i-1][j];k++) ans[i][j][k]=ans[i-1][j][k]; L[i][j]=L[i-1][j]; vis[i][j]=1; } for(int j=i;j<=N;j++) { if(vis[i][j]) continue; add(i,j); vis[i][j]=1; } } } void add(int k,int n)//高精度加法运算 { int len; for(int i=1;i<=L[k-1][n];i++) ans[k][n][i]=ans[k-1][n][i];//先复制一个序列 L[k][n]=L[k-1][n]; len=max(L[k][n],L[k][n-k]); int sum,overflow=0; for(int i=1;i<=len;i++){ if(L[k][n-k]<i){//根据两序列长度不同分类讨论 sum=ans[k][n][i]-'0'+overflow; } else { if(L[k][n]<i) {ans[k][n][i]='0';L[k][n]++;} sum=ans[k][n][i]-'0'+ans[k][n-k][i]-'0'+overflow; } ans[k][n][i]=sum%10+'0'; overflow=sum/10; } if(overflow){//最后还有进位 len=++L[k][n]; ans[k][n][len]=overflow+'0'; } return ; }
时间: 2024-10-04 10:42:49