过的很辛苦的一道题,大致思路其实也不是很久就想出来了,但是很让人无语的是,强悍的trick让我一直怀疑我的程序是不是读入的时候出错了。。
后来再次看讨论版(好吧,这不是好习惯。。),讨论版就提供了一组很有意思的数据:
0
0 0
0 1
0 0 0
0 0 1
0 1 1
0 1 2
200 30 75
输出为
1
1
1
1
1
0
0
2347163627458
好吧,说思路吧:
1.对一个数N,就是用dp方程:
f[S]+=f[S-a[i]] ,f[0]=1
对于两个数或者三个数,定义num[]数组为钱为S时的硬币数,可以这样:
num[S]=num[S-a[i]]+1
对于每个num[S],有1~S共S种的硬币数,这样就要定义一个结构体,里面包含硬币出现的大小和次数,即num[S]可以表示为:钱为S时,共有多少种不同硬币的放法,每种放法出现多少次。
假设两个数,N,L;
则只要判断num[N]里面出现L个硬币或更少的次数为多少,就可以了
三个数也同上面方法
对程序的优化就是开始把要用的全算出来
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <vector>
using namespace std;
#define LIM 310
#define MAXN 300
long long f[LIM];
int a[MAXN];
//
struct node{
long long value;
long long times;
node(int value,int times){
this->value=value;
this->times=times;
}
};
vector<node> num[LIM];
char str[30];
int choice;
int N,L,L1,L2;
long long Ans;
int search(vector<node> & v, long long x ){
for( vector<node>::iterator it=v.begin() ; it != v.end() ; it++ ){
if( it->value == x ){
return it-v.begin();
}
}
return -1;
}
void prepare(){
for(int i=1;i<=MAXN;++i){
a[i]=i;
}
f[0]=1;
for(int i=1;i<=MAXN;++i) f[i]=0;
for(int i=1;i<=MAXN;++i){
for(int j=a[i];j<=300;++j){
f[j]+=f[j-a[i]];
}
}
num[0].push_back(node(0,1));
for(int i=1;i<=MAXN;++i){
for(int j=a[i];j<=300;++j){
for( vector<node>::iterator it=num[j-a[i]].begin() ; it != num[j-a[i]].end() ; ++it){
//num[j].push_back(*it+1);
int pos=search( num[j] , it->value+1 );
if( pos == -1 ){
num[j].push_back( node(it->value+1,it->times) );
}
else{
num[j][pos].times+=it->times;
}
}
}
}
/*for(int i=1;i<=10;++i){
printf("i=%d f[i]=%d\n",i,f[i]);
}
for(int i=1;i<=10;++i){
for( vector<int>::iterator it=num[i].begin() ; it != num[i].end() ; ++it){
printf("i=%d *it=%d\n",i,*it);
}
}*/
}
void input(){
if( fgets(str,30,stdin) == NULL ){
exit(0);
}
int len=strlen(str);
choice=1;
for(int i=0;i<len;++i ){
if( str[i] == ' '){
choice++;
}
}
if( choice == 1 ){
sscanf(str,"%d",&N);
}
else if( choice == 2){
sscanf(str,"%d%d",&N,&L);
}
else{
sscanf(str,"%d%d%d",&N,&L1,&L2);
}
}
void dp1(){
Ans=f[N];
}
void dp2(){
Ans=0;
if( N==0 ){
Ans=1;
return;
}
if( L==0 ){
return;
}
if( L > N ){
L=N;
}
for(vector<node>::iterator it=num[N].begin() ; it != num[N].end(); it++){
if( (it->value) <= L ){
Ans+=(it->times);
}
}
}
void dp3(){
Ans=0;
if( L1==0 && N==0 ){
Ans=1;
return;
}
if( L1 > N ){
return;
}
if( L1 == 0 ){
L1 = 1;
}
if( L2 > N ){
L2=N;
}
for(vector<node>::iterator it=num[N].begin() ; it != num[N].end(); it++){
if( (it->value) >= L1 && (it->value) <= L2 ){
Ans+=(it->times);
}
//printf("v=%d t=%d\n",it->value,it->times);
}
}
void solve(){
//printf("c=%d\n",choice);
if( choice == 1 ){
dp1();
}
else if( choice == 2){
dp2();
}
else{
dp3();
}
printf("%lld\n",Ans);
}
int main()
{
prepare();
while(true){
input();
solve();
}
return 0;
}
此程序时间复杂度是O(n^4),
但是如果不搜索,每次只是判断,时间复杂度可以优化为O(n^3)(后来才想到的)
(n == 300 )
这样原来跑了0.752s,
现在只要0.096s(这就是时间复杂的差距啊。。)