poj1010搜索

问题重述:

       给出n种邮票,每种邮票有自己的面值(面值可能重复)

       指定m种“总面值”,对每种“总面值”,求解满足如下条件的组合以达到该“总面值”

(1)       所用邮票在n种中可以重复选取

(2)       所用邮票张数〈=4

(3)       尽量多的使用那个不同种类的邮票 Max (Stamp Types)

(4)       若有多种方案满足(3),则选取张数最小的一种方案 Min (Stamp Num)

(5)       若有多种方案满足(3)(4),则选取“最大面额”最高的一种方案。 Max(Heightest Value)

(6)       若有多种方案满足(3)(4)(5) 则输出 “tie”

题目分析:

(1)    算法定位:

从题目的条件可知,此题必须遍求所有方案以求解,因此采用搜索的方法是非常合理的,因为题目带由一定的递推性,也可以尝试动态规划的方法.

(2)    问题优化与简化

对于搜索型题目,关键的优化就是减少重复计算,本题可由3方面的简化,避免重复性计算.

(1)    对于面额相等的邮票,可以限制在1~5张,多余的可以不处理,例如这样的输入:

1 1 1 1 1 1 1 1 0     8个1

4 0

可以简化成为:

1 1 1 1 1 0             5个1

4 0

他们的解是相同的

(2)    搜索求解时约定, 后一张邮票面额>=前面张邮票的面额,即如下的6种情况:

1 2 3        2 3 1      3 12       1 3 2      3 2 1      2 1 3

只需搜索判断一种,即 1 2 3 的情况

(3)    对于给定的n种邮票,遍历4张邮票的所有可达“总面额”的解,当m种指定面额给出,只需插标输出即可,不需要重复m次类似的搜索。(因为m次的搜索中,很多是重复计算的)

(3)算法介绍

       (1) 搜索方法一: (base on 史诗’s program)

                     主要思路:

                            4重循环,枚举所有可能,依据题目条件保留最优解。

For(i=1;i<=total_stamps;i++)

For(j=i;j<=total_stamps;j++)

For(k=j;k<=total_stamps;k++)

For(l=k;l<=total_stamps;l++)

{

更新最优解

}

                     优化:

                            使用优化方案(1)(2), 修改后可以加上优化方案(3)

                     评价:

                            编程复杂度低,代码少,运行时空效率高,比赛时候推荐使用

                            但是扩展性受限制,若题目给的是任一k张而不是4,则必须大量修改代码

       (2) 搜索方法二: (base on hawking’s program)

                     主要思路:

                            递归深度搜索,遍历所有k张邮票的可达面额的解,保留每种面额的最优

                     解,查表输出指定面额的解,并给定k=4,即为题目要求的情况。

              Void Solve(int deep)

              {

更新当前方案所达面值的最优解

if (deep>4) return;

                     for(int s=now[deep-1];s<=total_stamp;s++)

                     {

                            now[deep]=s;

                            solve(deep+1);

}

}

                     优化:

                            使用优化方案(1)(2)(3)进行截枝

                     评价:

                            深度搜索的经典模板程序,推荐学习,几乎所有的搜索都可以套用的模板

                            编程复杂度中,运行时空效率高,通用性强一些,但代码可能稍为多一些

       (3) 动态规划: (base on my program)

                     主要思路:

                            定义:int anstype[k][i][j] 表示:

                                   用前k种邮票,选i张,达到面额为j,最多用多少种不同的类型邮票。

                            定义:bool tied[k][i][j] :

k,i,j 定义如上,tied 表示 anstype[k][i][j] 的最优值是否有多种方案.即同时满足题目的条件(3)(4)时是否多种

                     定义:bool anstied[k][i][j] :

由于 tied 限制不够强,必须多一个anstied 表示同时满足条件(3)(4)(5)是否有多种方案

                     递推关系:

(1)         

anstype[k][i][j]=Max{anstype[k-1][i-w][j-w*value[k]]+1}   1<=w<=4, i-w>0, j-w*value[k]>=0

(2)

在anstype 的更新最大值的同时,可以判断,若某个w 使得

anstype[k][i][j]==anstype[k-1][i-w][j-w*value[k]]+1 则

tied[k][i][j]=ture;

anstype[k][i][j]<anstype[k-1][i-w][j-w*value[k]]+1 则

tied[k][i][j]=false

(3)

至于anstied 可以这样求得:

anstype[k][i][j]<anstype[k-1][i-w][j-w*value[k]]+1时

anstied[k][i][j]=tied[k-1][i-w][j-w*value[k]]

anstype[k][i][j]==anstype[k-1][i-w][j-w*value[k]]+1时

       anstied=true;

              主要优化:

                     使用优化方案(1)(2)(3)

                     空间优化,由于[k][i][j]只与[k-1][i][j]有关,所以只需2维数据[i][j]就可以了

这是动态规划最常用的空间优化,另外的一些是压缩存储技巧,主要的是用位运算存储,本题没有用上,不过以后有些题目会常用。

              评价:

                     编程复杂度 中,运行时间少,可以承受大规模数据,规模可扩展,

但是空间要求量大,必须使用压缩技巧,这样编程复杂度会增大,推荐研究算法用,比赛不用

总结:

       类似本题的问题很多,光stamps问题多年来就一直存在各种变种,sticks问题与本体类似,通用的算法都是搜索,练熟搜索模板是基础,简化问题、掌握剪枝技巧是解题关键。某些特殊问题可以用动态规划,但是并不是所有的都可以,例如sticks我试过是不能用动态规划的,而且比赛时候,思考会浪费时间,所以推荐比赛时用搜索。(不排除某些题目用搜索通过不了的大数据,必须用动态规划解决,所以要估计好时间空间,选用稳妥的算法)

#include <iostream.h>

#include <strstrea.h>

#include <string.h>

int stamp[1006]; // from 1 to 25, stamp[0] means none

int s[4]; // stamps sold

int n; // number of stamp types

int m; // customer request

int k; // number of stamps sold

int t; // types of stamps sold

int max; // highest single-value stamp value

int curK; // numbers of stamps sold of current best solution

int curT; // types of stamps sold of current best solution

int curM; // highest single value of current best solution

int curS[4]; // current best solution

bool curTie; // if there is tie best solutions

int temp;

bool ok(int temp) // there are no maore than 4 stamps value=temp

{

       int i,j;

       for (i=1,j=0;i<n;i++) if (stamp[i]==temp) j++;

       return (j<5);

}

void Dodo(){

    for ( s[0]=0; s[0]<n; s[0]++)

        for ( s[1]=s[0]; s[1]<n; s[1]++)

            for ( s[2]=s[1]; s[2]<n; s[2]++)

                for ( s[3] = s[2]; s[3]<n; s[3]++) {

                    if ( stamp[s[0]]+stamp[s[1]]+stamp[s[2]]+stamp[s[3]] != m ) continue;

                    k = 0;

                    t = 0;

                    max = 0;

                    if ( stamp[s[0]] ) {k++; t++; if ( stamp[s[0]]>max ) max = stamp[s[0]];}

                    if ( stamp[s[1]] ) {k++; if ( s[1]>s[0] ) t++; if ( stamp[s[1]]>max ) max = stamp[s[1]];}

                    if ( stamp[s[2]] ) {k++; if ( s[2]>s[1] ) t++; if ( stamp[s[2]]>max ) max = stamp[s[2]];}

                    if ( stamp[s[3]] ) {k++; if ( s[3]>s[2] ) t++; if ( stamp[s[3]]>max ) max = stamp[s[3]];}

                    if ( t<curT ) continue;

                    if ( t>curT ) {

                        curT = t;

                        curK = k;

                        curM = max;

                        curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];

                        curTie = false;

                        continue;

                    }

                    if ( k>curK ) continue;

                    if ( k<curK ) {

                        curT = t;

                        curK = k;

                        curM = max;

                        curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];

                        curTie = false;

                        continue;

                    }

                    if ( max<curM ) continue;

                    if ( max>curM ){

                        curT = t;

                        curK = k;

                        curM = max;

                        curS[0]=s[0]; curS[1]=s[1];curS[2]=s[2];curS[3]=s[3];

                        curTie = false;

                        continue;

                    }

                    curTie = true;

                }

    if ( curT == -1 ) {

        cout<<m<<" ---- none"<<endl;

        return;

    }

    cout<<m<<" ("<<curT<<"):";

    if ( curTie ) {

        cout<<" tie"<<endl;

        return;

    }

    for ( int i=0; i<4; i++)

        if ( curS[i]>0 ) cout<<' '<<stamp[curS[i]];

    cout<<endl;

}

int main() {

    char strs[255];

    cin.getline(strs,255);

    while ( strlen(strs) ) {

        istrstream in1(strs);

        s[0] = 0;

        n = 1;

        while ( in1 ) {

            in1>>temp;    //modify by duoshute

                     if (ok(temp)) // there are no maore than 4 stamps value=temp

                     {

                            stamp[n++]=temp;

                            if ( stamp[n-1] == 0 ) break;

                     }

        }

        n--;

        cin.getline(strs,255);

        istrstream in2(strs);

        while ( in2 ) {

            in2>>m;

            if ( m == 0 ) break;

            curK = 100;

            curT = -1;

            curM = -1;

            curTie = false;

            Dodo();

        }

        cin.getline(strs,255);

    }

    return 0;

}

#include "iostream.h"

#include "string.h"

#include "stdlib.h"

#define Max 200

int a[Max];

int b[Max];

bool ask[Max];

bool asktied[5][Max];

int anst[5][Max];

char ans[4][Max][4];

bool tied[5][Max];

int i,j,k,l,n,m,max,p,q;

int temp;

bool ok(int temp) // there are no maore than 4 stamps value=temp

{

       int i,j;

       for (i=1,j=0;i<n;i++) if (a[i]==temp) j++;

       return (j<5);

}

int compare(const void * a,const void *b) {return *(int *)a-*(int *)b;}

void work(int oldt,int newt,int oldh,int newh,int l,int j,int k,int p)

{

       int s;

       if (newt<oldt) return;

       if (newt==oldt)

       {

              tied[p+k][l]=true;

              if (ask[l] && newh==oldh) asktied[p+k][l]=true;

              if (newh<=oldh) return;

       }

       else tied[p+k][l]=tied[p][j];

       if (ask[l])       asktied[p+k][l]=tied[p][j];

       //ok;

       anst[p+k][l]=newt;

       memcpy(ans[p+k-1][l],ans[p-1][j],sizeof(ans[p+k-1][l]));

       for (s=0;s<k;s++)   ans[p+k-1][l][p+s]=newh;

}

void main()

{

       for(;;)

       {

              if (cin.eof()) break;

              for (n=0;;)

              {

                     if (!(cin>>temp)) break;

                     if (ok(temp)) // there are no maore than 4 stamps value=temp

                     {

                            a[++n]=temp;

                            if (a[n]==0) break;

                     }

              }

              n--;if (n<=0) break; //end of file

              qsort(a,n+1,sizeof(int),compare);

              memset(ask,false,sizeof(ask));

              for (m=0,max=0;;)

              {

                     cin>>b[++m];if (b[m]==0) break;if (b[m]>max) max=b[m];ask[b[m]]=true;

              }

              m--;

              for (j=0;j<=4;j++) for (i=0;i<=max;i++) anst[j][i]=-1;

              anst[0][0]=0;

              memset(ans,0,sizeof(ans));

              memset(tied,false,sizeof(tied));

              memset(asktied,false,sizeof(asktied));

              for (i=1;i<=n;i++)

              {

                     for (j=max;j>=0;j--) 

                            for (p=0;p<=4;p++) if (anst[p][j]>=0)

                                   for (k=1;k<=4;k++) if (j+a[i]*k<=max) if (p+k<=4)

                                   {

                                          l=j+a[i]*k;

                                          // work(oldt,newt,oldn,newn,oldh,newh,l);

                                          work(anst[p+k][l],anst[p][j]+1,ans[p+k-1][l][p-1],a[i],l,j,k,p);

                                   }

              }

              for (i=1;i<=m;i++)

              {

                     cout<<b[i]<<' ';

                     l=0;p=0;

                     for (j=4;j>=1;j--)

                     {

                            for (k=1;k<=4;k++) if (anst[k][b[i]]==j) {l=j;p=k;break;}

                            if (l>0) break;

                     }

                     if (l==0) {cout<<"---- none/n";continue;}

                     cout<<'('<<anst[k][b[i]]<<"): ";

                     if (!asktied[k][b[i]])

                     {

                            for (j=1;j<=k;j++) {cout<<(int)ans[k-1][b[i]][j-1];if (j<k) cout<<' ';}

                            cout<<endl;

                     }

                     else cout<<"tie/n";

              }

       }

}

/*

by hawking@pku

*/

#include<iostream.h>

#include<search.h>

#include<memory.h>

const int MAX_STAMP_TYPES =100;

const int MAX_TOT_VALUE = 100;

const int MAX_CUSTOMER=100;

int s[MAX_STAMP_TYPES],s_size,cus[MAX_CUSTOMER],c_size,maxc,count[100];

bool custom[MAX_TOT_VALUE];    

class Combine{

public:

    int tot;

    int ts;//types of stamps

    int f;//flag 1 can 2 tie,0 can't

    int s[4];//each stamps used here

    int sc;//used stamps number

    int max;//single maxed stamp value;

public:

    bool isHave(int type){

        for(int i=0;i<sc;i++)

            if(s[i]==type)return true;

        return false;

    }

    void findMax(){

        max=0;

        for(int i=0;i<sc;i++)

            if(::s[s[i]]>max)max=::s[s[i]];

    }

    void addStamp(int type){

        if(!isHave(type))ts++;

        s[sc++]=type;

        tot+=::s[type];

        if(::s[type]>max)max=::s[type];

    }

    void removeLastStamp(int type){

        sc--;

        if(!isHave(type))ts--;

        tot-=::s[type];

        findMax();

    }        

}currCus[MAX_TOT_VALUE],c;

int com(const void * a,const void *b)

{

    return *((int *)a)-*((int*)b);

}

void init()

{

    maxc=c_size=s_size=0;

    memset(custom,0,MAX_TOT_VALUE*sizeof(bool));

    memset(count,0,100*sizeof(int));

}

bool read()

{

    init();

    int t;

    cin>>t;

    if(!cin)return false;

    while(t!=0){

        if(count[t]++<5)s[s_size++]=t; //modify by duoshute here

        cin>>t;

    }

    qsort(s,s_size,sizeof(int),com);

    cin>>t;

    while(t!=0){

        cus[c_size++]=t;

        custom[t]=true;

        if(t>maxc)maxc=t;

        cin>>t;

    }

    return true;

}

int a[4],b[4];

bool same(int *t1,int *t2,int n)

{

    memcpy(a,t1,n*sizeof(int));

    memcpy(b,t2,n*sizeof(int));

/*    qsort(a,n,sizeof(int),com);           //omited by duoshute

    qsort(b,n,sizeof(int),com); */

    return memcmp(a,b,n*sizeof(int))==0;

}

void solve(int ss,int last) //int last is added by duoshute

{

    if(custom[c.tot])

    {

        if(!currCus[c.tot].f

                ||c.ts>currCus[c.tot].ts

                ||c.ts==currCus[c.tot].ts&&c.sc<currCus[c.tot].sc

                ||c.ts==currCus[c.tot].ts&&c.sc==currCus[c.tot].sc&&c.max>currCus[c.tot].max)

        {

            currCus[c.tot]=c;

            currCus[c.tot].f=1;

        }

        else if(c.ts==currCus[c.tot].ts&&c.sc==currCus[c.tot].sc&&c.max==currCus[c.tot].max)

        {

            if(!same(c.s,currCus[c.tot].s,c.sc))

                currCus[c.tot].f=2;

        }

    }

    if(c.sc>=4||c.tot>maxc)return;

    for(int i=last;i<s_size;i++) 

    {

        c.addStamp(i);

        solve(ss+1,i);

        c.removeLastStamp(i);

    }

}

void main()

{

    while(read())

    {

        memset(&c,0,sizeof(Combine));

        memset(currCus,0,MAX_TOT_VALUE*sizeof(Combine));

        solve(0,0);

        for(int i=0;i<c_size;i++)

            if(currCus[cus[i]].f==0)

                cout<<cus[i]<<" ---- none"<<endl;

            else if(currCus[cus[i]].f==2)

                cout<<cus[i]<<" ("<<currCus[cus[i]].ts<<"): "<<"tie"<<endl;

            else 

            {

                cout<<cus[i]<<" ("<<currCus[cus[i]].ts<<"):";

                qsort(currCus[cus[i]].s,currCus[cus[i]].sc,sizeof(int),com);

                for(int j=0;j<currCus[cus[i]].sc;j++)

                    cout<<' '<<s[currCus[cus[i]].s[j]];

                cout<<endl;

            }

    }

}

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值