poj 1010 邮票问题 DFS

来源:互联网 发布:js onmousemove 编辑:程序博客网 时间:2024/06/04 19:58

原文地址:http://hi.baidu.com/you289065406/blog/item/8c4395033c085d0b738b6502.html

大致题意:

题意比较难懂。大致如下:

第一行数字是邮票的面值,每一个数字就是一个不同的种类,哪怕面值相同。以0结束。

第二行数字是顾客所需要的邮票总面值。每个数字就是一个顾客的需求,以0结束。

每两行是一组case。以EOF结束输入。

 

顾客是集邮爱好者,所以你必须尽可能的给他不同种类的邮票。

但是一位顾客最多只能拿4张邮票。

显然,我们拥有的邮票就是第一行中的数据。

 

解题思路:

DFS寻找所有的解,再逐一比较寻找最优解,剪枝是关键。

 

关于tie

满足顾客需求的解就是可行解。

邮票种类最多的可行解为最优。

如果存在两个以上的最优解的邮票种类是一样的,张数最少的更优

张数也一样的话,这些最优解中最大面值较大的更优。

若邮票种类、张数、最大面值三者都分别相同,则认为这些最优解相同,输出tie

没有解就是none

 

做法大致有三种。

可以去参考这位牛人的三种方法:http://blog.csdn.net/cugbliang/article/details/2742242

 

1、枚举。反正最多拿4张,可以4重循环暴搜最优解。

2DFS。每次搜索后,如果有解,更新最优解,关键在剪枝。

3、三维DP。这个没怎么研究,不太懂……参考上面的网址吧。。

 

我用的是第二种DFS

 

剪枝:

1、最多拿四张邮票,如果同一面值的邮票种类超过5,以5计算。

为什么不以4计算呢?因为tie

2、若DFS的深度超过了4,那么就返回。(最多四张邮票)

3、技巧剪枝:

先对输入的邮票按面值升序排序,DFS到面值k时,不再搜索面值<k的邮票。

  同时排序也是为了保证DFS的最优解的邮票种类最多。


以上是转载的。还有这位大神的代码也贴出:

#include<iostream>#include<algorithm>using namespace std;const int size=26;int value[size];//第i种邮票的面值value[i]int pv;//value的指针int tim[size];//标记第i种邮票被分配过的次数bool flag;//标记是否已经出现过解bool flag_tie;//标记是否为tieint solve[6];// solve[0]:邮票张数 ,solve[5]邮票的种数  solv[1...4]持有的面值,0代表不持有int bestsolve[6];//最优解int max4(int *s){  int a=s[1]>s[2]?s[1]:s[2];  int b=s[3]>s[4]?s[3]:s[4];  return a>b?a:b;}void best(int num,int type){  bestsolve[0]=num;  bestsolve[5]=type;  for(int k=1;k<=4;k++)  bestsolve[k]=solve[k];  return;}void dfs(int need,int num,int type,int pre) //need:总面值 num:邮票张数 type:邮票种数 pre:当前面值 {  if(num==5) return;//剪枝,顾客持有邮票张数不超过4  if(need==0)  {    if(!flag)    {      if(type==bestsolve[5])//最优解的种类type相同      {        if(num==bestsolve[0])//最优解的张数相同         {          int maxs=max4(solve);//solve的最大面值          int maxbs=max4(bestsolve);//bestsolve的最大面值          if(maxs==maxbs)//存在多个最优解           flag_tie=true;          else if(maxs>maxbs)//种类和张数都相同的情况下,最大面值较大者优先           {            flag_tie==false;            best(num,type);          }        }        else if(num<bestsolve[0])//种类相同的情况下,张数少的优先        {          flag_tie=false;          best(num,type);        }       }       else if(type>bestsolve[5])//种类多的解优先       {        flag_tie=false;        best(num,type);      }    }    else    {      flag=true;      best(num,type);    }        return ;   }      for(int i=0;i<pv;i++)   {     if(i<pre)//剪枝,不重复搜索比当前面值小的邮票,同时避免错误的tie     continue;     if(need>=value[i])     {       solve[num+1]=value[i];       if(tim[i]!=0)       {         tim[i]++;         dfs(need-value[i],num+1,type,i);       }       else       {         tim[i]++;         dfs(need-value[i],num+1,type+1,i);       }              solve[num+1]=0;//回溯        tim[i]--;     }     else return;//value已排序    }   return;}int main(){  while(true)  {    pv=0;    int type[size]={0};//面值为i的邮票的种数type[i]        int tmp;    while(1)    {      if(scanf("%d",&tmp)==EOF)//写在循环条件里怎么就TLE呢        exit(0); //这两句注释掉 机器就跑不出来。。。       if(tmp==0)  break;      if(type[tmp]<5)//剪枝,如果一样的面值出现5次一上那么以后的就不要记录       {        type[tmp]++;        value[pv++]=tmp;      }    }     sort(value,value+pv);//要使分配的邮票的种类尽可能多                         //只需在搜索前把邮票面值升序排序,从最小面值开始搜索        int need;    while(scanf("%d",&need)!=EOF&&need)    {      flag=false;      flag_tie=false;      memset(solve,0,sizeof(solve));      memset(bestsolve,0,sizeof(bestsolve));      memset(tim,0,sizeof(tim));            dfs(need,0,0,0);      cout<<need;      //printf("%d",need);      if(bestsolve[0]==0)      cout<<" ---- none"<<endl;      else      {        cout<<" ("<<bestsolve[5]<<"):";        if(flag_tie)        cout<<" tie"<<endl;        else        {          sort(bestsolve+1,bestsolve+5);          for(int i=1;i<=4;i++)          {            if(bestsolve[i]==0)            continue;            cout<<" "<<bestsolve[i];          }          cout<<endl;        }      }    }                        }  return 0;}

下面是本菜鸟,照葫芦画飘弄的。。。

#include<iostream>#include<algorithm>#define size 26using namespace std;int value[size];int type[size];int tim[size];int solve[6];int bestsolve[6];bool flag,flag_tie;int need;int pv;int maxs(int *s)//找最大的面值 {  int a,b;  a=s[1]>s[2]?s[1]:s[2];  b=s[3]>s[4]?s[3]:s[4];  return a>b?a:b;}void best(int num,int type)//更新最优解 {  bestsolve[0]=num;  bestsolve[5]=type;  for(int k=1;k<=4;k++)  bestsolve[k]=solve[k];}void dfs(int need,int num,int type,int pre){  if(num==5)  return;  //printf("%d %d %d %d \n",need,num,type,pre);  //system("pause");  if(need==0)//如果搜到了一种情况,进行判断   {    if(!flag)    {      if(type==bestsolve[5])      {        if(num==bestsolve[0])        {          int max1=maxs(solve);          int max2=maxs(bestsolve);          if(max1==max2)          {            flag_tie=true;            best(num,type);          }          else if(max1>max2)          best(num,type);        }        else if(num<bestsolve[0])        {          flag_tie=false;          best(num,type);        }      }      else if(type>bestsolve[5])      {        flag_tie=false;        best(num,type);      }    }    else    {      flag=true;      best(num,type);    }        return;      }    for(int i=0;i<pv;i++)  {    if(i<pre)  continue;//搜比它大的面值    //printf("i=%d\n",i);    if(need>=value[i])    {      if(tim[i]!=0)      {        tim[i]++;        solve[num+1]=value[i];        //printf("1--%d\n",value[i]);        dfs(need-value[i],num+1,type,i);      }      else      {        tim[i]++;        solve[num+1]=value[i];        //printf("2--%d\n",value[i]);        dfs(need-value[i],num+1,type+1,i);      }            tim[i]--;      solve[num+1]=0;//回溯          }     else return;  }  return;}int main(){   int tmp;   while(1)   {     pv=0;     memset(type,0,sizeof(type));     memset(value,0,sizeof(value));     while(1)     {       while(scanf("%d",&tmp)==EOF)       exit(0);       if(tmp==0)  break;       type[tmp]++;       if(type[tmp]<5)       value[pv++]=tmp;     }     sort(value,value+pv);      //for(int k=0;k<pv;k++)   //cout<<" "<<value[k];     while(scanf("%d",&need)!=EOF&&need)     {       memset(tim,0,sizeof(tim));       memset(solve,0,sizeof(solve));       memset(bestsolve,0,sizeof(bestsolve));       flag=flag_tie=false;       dfs(need,0,0,0);     //if(flag)  printf("flag");     //if(flag_tie)  printf("flag_tie");     //for(int g=0;g<6;g++)     //cout<<" "<<bestsolve[g];     //printf("结束");     //system("pause");     //sort(bestsolve+1,bestsolve+4);       printf("%d ",need);       if(bestsolve[0]==0)       {         printf("---- none\n");       }       else if(flag_tie)       {         printf("(%d): tie\n",bestsolve[5]);       }       else        {         printf("(%d):",bestsolve[5]);         for(int g=1;g<=4;g++)         {           if(bestsolve[g])           printf(" %d",bestsolve[g]);         }         printf("\n");       }     }   }   return 0;}