pku1014----dividing(平分钻石,动态规划--剪枝)

352K0MSG++1077B2009-01-15 12:35:04

判断给定的钻石价值能否均分。

 

给一个bool数组它表示能分出的价值数<总价值的一半(如:总价值为8,且可分成3和5两份,那么f[3]=true)

对于每个价值数(遍历a[1]到a[6])对于bool数组里面元素为真的下标,使其加上一个新给的价值,并赋予其值为真。

如f[2]为真,表示可以分出价值2的一份钻石,这时另一份钻石里面还有价值为1的钻石(即a[1]!=0)

那么f[3]=true(3=2+1)表示可以分出价值3的一份钻石。

注意考虑的顺序,即循环的嵌套关系。。。。。。。。。。。

优化:

1.对于总价值为奇数的肯定不可以分。

2.

if(j+val*i>mid||f[j+val*i])
       break;

前面:由于考虑的只有前mid项就可以,只要判断一份价值为mid的钻石能否分出,大于mid的判断不用。

后面:

假设现在flag[]的序列是这样的:1 1 0 1 1 0 1 1 0 1,当前考察的是 i=3array[i]=5,就是要在这个基础上加上53,按照程序的意思,从最后一个1开始依此加上3,将其值变为1,一共加上5个,然后在倒数第二个1上依此加上3,将其值变为1,一共加上5个,这个过程不会遇见flag=1的情况,给倒数第三个1依此加3的时候,会遇到:flag=1,这个时候就可以break了,因为这时候还需要加的43都在最后一个153时候加过了,这里要注意的是,给每个1加上3时候,只会遇到旧的”flag=1,不会遇到新增加的flag=1,而旧的1已经加过了array[i]i,所以就不用加了,直接退出就行了。---------------------【取自网上某位大牛的解题报告】

这个剪枝很强大,如果不加的话,会时间超时。。。

 

代码如下:

 

ContractedBlock.gif ExpandedBlockStart.gif Code
#include<stdio.h>
#include
<string.h>
int a[7],caseno=0,mid;
bool f[120011];
int input()
{
    
int i,flag=1;
    
for(i=1;i<=6;i++){
        scanf(
"%d",&a[i]);
        
if(a[i]!=0)
            flag
=0;
    }
    caseno
++;
    
if(flag)    return 0;
    
else    return 1;
}
void dp()
{
    
int val,index=0,i,j;
    memset(f,
0,sizeof(bool)*(mid+10));
    f[
0]=1;
    
for(val=1;val<=6;val++){
            
if(a[val]==0)
                
continue;
            
for(j=index;j>=0;j--){
                
if(f[j]){
                    
for(i=1;i<=a[val];i++){
                        
if(j+val*i==mid){
                            printf(
"Collection #%d:\nCan be divided.\n\n",caseno);
                            
return ;
                        }
                        
else if(j+val*i>mid||f[j+val*i])
                            
break;
                        
else
                            f[j
+val*i]=1;
                    }
                }
            }
            index
+=val*a[val];
            
if(index>mid)    index=mid;
    }
    printf(
"Collection #%d:\nCan't be divided.\n\n",caseno);

}
void process()
{
    
int i;
    
int sum;
    
for(i=1,sum=0;i<=6;i++)
        sum
+=a[i]*i;
    
if(sum%2!=0)
    {
        printf(
"Collection #%d:\nCan't be divided.\n\n",caseno);
        
return ;
    }
    
else{
        mid
=sum/2;
        dp();
    }
}
int main()
{
    
while(input()){
        process();
    }
    
return 0;
}

转载于:https://www.cnblogs.com/pandy/archive/2009/01/15/1376297.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值