解题报告(一):uva 10313 - Pay the Price (dp)

     过的很辛苦的一道题,大致思路其实也不是很久就想出来了,但是很让人无语的是,强悍的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(这就是时间复杂的差距啊。。)


  

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值