POJ 1014

据传说,这个是个背包问题,于是我就做了……

题目大意:

给定六个正整数 a1,a2,a3,a4,a5,a6,求一个分割(k1,k2,k3,k4,k5,k6)使得,k1*1+k2*2+...+k6*6=(a1-k1)*1+(a2-k2)*2+...+(a6-k6)*6,其中ai>=ki>=0,且ai,ki均为正整数。

-----------------------------------------------我是华丽的分割线---------------------------------------------

最后AC的解法我依旧沿用的是搜索DFS+剪枝的思路,并不是最优的解法。网上有动态规划的解法(动态规划的解法我绞尽脑汁也没有想出来,虽然我知道这个肯定有动态规划的解法)……没有进步啊……

搜索不用说了,优先拿价值最大的,如果发现拿不了了,就退一个,再拿。这种算法会遍历所有的可能。所以当解法为不可能的时候,这算法就会跑很长时间。第一次就是TLE。

插一段:关于特殊的测试数据:

这个问题实际上更广的是求解 1*a+2*b+3*c+4*d+5*e+6*f=K在某个范围的整数解。所以呢,想要无解,充分条件是是gcd(k和系数)!=1,所以呢

0 6666 0 6666  0 6665

这是一无解的。而且组合也足够的多。

再说算法的剪枝:

1.价值总和是奇数,那就不用考虑了。

2.当需要的价值大于所有价值小于等于当前石头的石头的价值总和的时候,这条路也肯定走不通

3.算法会遍历很多重复的点,需要用一个变量记录这些重复的点,当再次到达的时候,直接返回结果。(这条记录方法我想了半天,在内存限制的情况下)(典型的可以用动态规划的特征:重复子问题)

所以贴代码吧:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int a[6];
int b[6];
int valueI[60001];
int pickUpM(int i,int value){
   
   int j;
   int temp,vTemp;
   
   if ((((valueI[value]>>i)&1)==1)) { return 0;}  //判断该点是否来过
   
   if (value==0) return 1;
   
   if (i==0){
     if (value>a[i]) {valueI[value]|=(1<<i);return 0;}
     else {a[i]=a[i]-value;return 1;}
   }
   
   if (value>b[i]) {valueI[value]|=(1<<i);return 0;}
   
   temp=value/(i+1);
   j=temp>a[i]?a[i]:temp;
   vTemp=value-(i+1)*j;
   for (;j>=0;j--){ 	 	 	 
   	 a[i]=a[i]-j;   	 	 	 
   	 if (!pickUpM(i-1,vTemp)){   	 	   
   	   a[i]+=j;
   	 }
   	 else {
   	   return 1;
   	 }
     vTemp+=(i+1);
   }
   valueI[value]|=(1<<i);
   return 0;
}

int main(){
  
  int i,k,sum;
  sum=0;
  for (i=0;i<6;i++){
    scanf("%d",&(a[i]));
    sum+=(a[i]*(i+1));
    b[i]=sum;
  }
  k=1;
  memset(valueI,'\0',60001*sizeof(int)); 
    
  while(sum!=0){

    memset(valueI,'\0',60001*sizeof(int)); 
    
    if ((sum%2==0)&&(pickUpM(5,sum/2))){ 	
    	printf("Collection #%d:\nCan be divided.\n",k);
    }
    else {
    	printf("Collection #%d:\nCan't be divided.\n",k);
    }
    
    printf("\n");
      
    k++;
    sum=0;
    for (i=0;i<6;i++){
      scanf("%d",&(a[i]));
      sum+=a[i]*(i+1);
      b[i]=sum;
    }
  }
}

以上代码在过测试数据的时候还是用了478MS,不知道还有什么地方可以优化的,所以。。。下面仔细学习学习动态规划算法去吧。。。似乎是传说中的《背包九讲》里面的内容吧…………

参考POJ1011的话,还有一个地方可以优化,对于存在解的情况,假设J是某种的石头价值,那么解中一定至少包含a[j]/2个这种石头(因为如果解存在,抽屉原理告诉我们在两边中肯定存在有一边这种石头是a[i]/2个),所以,不妨拿价值最大的石头开刀,如果对于价值最大的石头,我们只搜索包含a[j]~a[i]/2个这种石头的组合,那么搜索量可以减少一半了。

不过,参看高人的解法http://blog.csdn.net/lyy289065406/article/details/6661449,发现就算用搜索,也可以做到0MS……我要仔细研究一下奋斗

高人的算法很巧妙,虽然我还是不太理解,但是他用了一个很巧妙的方式去除了重复的搜索(例如 6+4+2和6+2+2+2)。







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值