【DFS】hdu 1455 Sticks

51 篇文章 0 订阅

http://acm.hdu.edu.cn/showproblem.php?pid=1455

分析:与hdu 1518类似:http://blog.csdn.net/killua_99/article/details/9569709

但明显多了各种剪枝,主要是对题目分析,明白:这些木棒都由相同长度得来,1)可能原长度一定是总和的约数,2)最小可能是从最大的木棒开始计算 etc

测试数据:http://poj.org/showmessage?message_id=172549

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;

const int NM=70;
int vis[NM],a[NM];
int c,flag,n;

bool comp(int A,int B)
{
	if(A>B) return 1;
	else return 0;
}

void DFS(int stick,int ssum,int ans,int k)
{
	if(ans==stick)
	{
		ssum++;
		ans=k=0; //注意把k也更新了
		if(ssum==c)
			flag=1;
	}
	if(flag) return;
	for(int i=k;i<n;i++)
	{
		if(!vis[i]&&stick>=ans+a[i])
		{
			ans+=a[i];
			vis[i]=1;
			DFS(stick,ssum,ans,i+1);
			ans-=a[i];
			vis[i]=0;
			
			if(k==0||flag) return;  /*最重要的剪枝:如果第一根木棒不能形成组合,那么这组数一定不成立*/
			while(a[i+1]==a[i]) i++;  //防止重复搜索
		}	
	}
}

int main()
{
	int sum,i;
	//freopen("out.txt","w",stdout);//文件
	while(cin>>n&&n)
	{
		sum=0;
		for(i=0;i<n;i++)
		{
			scanf("%d",&a[i]);
			sum+=a[i];
		}
		if(n==1)  //
		{
			printf("%d\n",sum);
			continue;
		}
		sort(a,a+n,comp);  //从较长的木棒开始搜索(因为可能几个较小木棒结合成功,本来它们分别要与较大木棒结合),可以减少回溯次数
		for(i=a[0];i<=sum;i++)  //好像存在不砍木棒的情况
		{
			if(sum%i!=0) continue;
			memset(vis,0,sizeof(vis));
			c=sum/i;  //木棒原先个数
			flag=0;
			DFS(i,0,0,0);  //木棒原先长度;个数,长度,第几只木棒
			if(flag)
			{
				printf("%d\n",i);
				break;
			}
		}
	}
	return 0;
}


再次尝试:

不排序与排序的效率比较:


#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;

const int NM=70;
int a[NM],n,temp,num;
bool vis[NM],flag;

void DFS(int ans,int k,int res)
{
    if(flag) return;
    if(res==temp){
        ans++;
        res=k=0;
    }
    if(ans==num){
        flag=true;
        return;
    }
    for(int i=k;i<n;i++){
        if(!vis[i] && res+a[i]<=temp){
            vis[i]=1;
            DFS(ans,i+1,res+a[i]);
            vis[i]=0;
            if(k==0 || flag) return;
            while(a[i+1]==a[i]) i++;
        }
    }
}

int main()
{
    int mmax,i,sum;
    while(scanf("%d",&n)&&n)
    {
        sum=mmax=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            sum+=a[i];
            if(mmax<a[i]) mmax=a[i];
        }
        flag=false;
        for(i=mmax;i<=sum;i++)
        {
            if(sum%i==0){
                num=sum/i;temp=i;
                memset(vis,0,sizeof(vis));
                DFS(0,0,0);
                if(flag){
                    printf("%d\n",i);
                    break;
                }
            }
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值