poj 1010 邮票问题 DFS

原文地址: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;//标记是否为tie
int 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 26
using 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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
POJ 1321 排兵布阵问题可以使用 DFS 算法求解。 题目要求在一个 n x n 的棋盘上,放置 k 个棋子,其中每行、每列都最多只能有一个棋子。我们可以使用 DFS 枚举每个棋子的位置,对于每个棋子,尝试将其放置在每一行中未被占用的位置上,直到放置了 k 个棋子。在 DFS 的过程中,需要记录每行和每列是否已经有棋子,以便在尝试放置下一个棋子时进行判断。 以下是基本的 DFS 模板代码: ```python def dfs(row, cnt): global ans if cnt == k: ans += 1 return for i in range(row, n): for j in range(n): if row_used[i] or col_used[j] or board[i][j] == '.': continue row_used[i] = col_used[j] = True dfs(i + 1, cnt + 1) row_used[i] = col_used[j] = False n, k = map(int, input().split()) board = [input() for _ in range(n)] row_used = [False] * n col_used = [False] * n ans = 0 dfs(0, 0) print(ans) ``` 其中,row 代表当前尝试放置棋子的行数,cnt 代表已经放置的棋子数量。row_used 和 col_used 分别表示每行和每列是否已经有棋子,board 则表示棋盘的状态。在尝试放置棋子时,需要排除掉无法放置的位置,即已经有棋子的行和列,以及棋盘上标记为 '.' 的位置。当放置了 k 个棋子时,即可计数一次方案数。注意,在回溯时需要将之前标记为已使用的行和列重新标记为未使用。 需要注意的是,在 Python 中,递归深度的默认限制为 1000,可能无法通过本题。可以通过以下代码来解除限制: ```python import sys sys.setrecursionlimit(100000) ``` 完整代码如下:

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值