其实这道题就是简单的完全背包问题。从低到高推断出每种硬币的所有面额的解即可。当然,离线的话会慢一点。可以将问题需要求出的子问题全部求出来。下一个问题出现的时候,如果之前已经求解过则不必求解,否则在之前的基础上继续求解。原本觉得没什么好写的,关键是同样的方法用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 ;
}