AcWing167. 木棒&&洛谷P1120 小木棍 (剪枝我剪剪剪~)

芙芙镇楼~

这俩题一样,不过acwing上的是多组数据,洛谷上只有一组(但是数据很强)

这是acwing的题面

167. 木棒​​​​​​

乔治拿来一组等长的木棒,将它们随机地砍断,使得每一节木棍的长度都不超过 50 个长度单位。

然后他又想把这些木棍恢复到为裁截前的状态,但忘记了初始时有多少木棒以及木棒的初始长度。

请你设计一个程序,帮助乔治计算木棒的可能最小长度。

每一节木棍的长度都用大于零的整数表示。

输入格式

输入包含多组数据,每组数据包括两行。

第一行是一个不超过 64 的整数,表示砍断之后共有多少节木棍。

第二行是截断以后,所得到的各节木棍的长度。

在最后一组数据之后,是一个零。

输出格式

为每组数据,分别输出原始木棒的可能最小长度,每组数据占一行。

数据范围

数据保证每一节木棍的长度均不大于 50。

输入样例:
9
5 2 1 5 2 1 5 2 1
4
1 2 3 4
0
输出样例:
6
5

这是洛谷的题面

题目描述

乔治有一些同样长的小木棍,他把这些木棍随意砍成几段,直到每段的长都不超过 50。

现在,他想把小木棍拼接成原来的样子,但是却忘记了自己开始时有多少根木棍和它们的长度。

给出每段小木棍的长度,编程帮他找出原始木棍的最小可能长度。

输入格式

第一行是一个整数 n,表示小木棍的个数。
第二行有 n 个整数,表示各个木棍的长度 ai​。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 #1复制

9
5 2 1 5 2 1 5 2 1

输出 #1复制

6

说明/提示

对于全部测试点,1≤n≤65,1≤ai​≤50。

下面是分析讲解

本题 想要暴力搜索可要好好想想剪枝的过程。因为测试数据还是很多的,而且我们要确定一个最小的,符合题意的长度。就意味着我们要从小到大枚举长度len,对于每一个len,我们都要进行一通搜索,直到找到第一个符合题意的len,退出,所以运行复杂度可以想见,我们要进行重重剪枝。

下面是剪枝的想法

1.

 按从大到小排序//剪枝 先搜情况少的,这个搜索树的宽度就小,因为长的木棍可选择的情况少

2.如果i木棍失败了 ,后面遇到了与i等长的j木棍,则 直接判定失败

3.如果第一根就失败了,那么直接判定此情况失败、(反证法,如果这一根在第一个的位置判定失败,它被放在了后面的位置,则可以把它平移到第一个的位置上,则矛盾。中间的木棍就不一样了,可能因为恰好那一个组合不合理,换一种组合方案就成功了)

4.同理,最后一个失败,则也失败

5.在枚举的时候,正确答案一定是总长的因子

总之,看代码叭~

package dfs;
import java.util.ArrayList;
	import java.util.Arrays;
	import java.util.Comparator;
	import java.util.Scanner;
public class 木棒多组数值 {
	
		//思路 按从大到小排序//剪枝 先搜情况少的、
		//如果i木棍失败了 ,后面遇到了与i等长的j木棍,则 直接判定失败
		//如果第一根就失败了,那么直接判定此情况失败、//同理,最后一个失败,则也失败
		static Integer a[]=new Integer [70];
		static int len;
		static int n;
		static int num;
		static int sum;
		static int book[]=new int [70];
	 public static void main(String[] args) {
		 
		Scanner sc=new Scanner(System.in);
		while(sc.hasNext())
		{
			
			num=0;
			sum=0;len=0;
			for(int j=0;j<70;j++)
			{
				if(book[j]!=0) book[j]=0;
			}
		 n=sc.nextInt();
		 if(n==0) break;
		int max=0;
		
		for(int i=1;i<=n;i++)
		{
			a[i]=sc.nextInt();
			sum+=a[i];
			max=Math.max(max, a[i]);
		}//剪枝 先对可选择情况少的(大木棍)进行拼接
		Arrays.sort(a,1,n,new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o2-o1;
			}
		});
		for( len=max;len<sum;len++)
		{
			if(sum%len!=0) continue;//剪枝 因为符合题意的长度一定是sum的因子
		  num=sum/len;//当前长度下的拼接好的木棍个数
		  if(dfs(1,0,1)) break;
		}
		System.out.println(len);
		}
	}
	 //cnt 现在正在处理的木棒
	 //cur 现在的木棒长度
	 //x 当前正在拼的木棍的第一个可用的编号//为了保证木棍组成木棒的编号 是递减的,避免重复
	 static boolean dfs(int cnt,int cur,int x)
	 {
		// System.out.println(cnt);
		 if(cnt>=num) return true;
		 if(cur==len) return dfs(cnt+1,0,1);
		 //剪枝 记录已经失败的木棍的长度
		 int fail=0;
		 
		 for(int i=x;i<=n;i++)
		 {
			 if(book[i]==0&&cur+a[i]<=len&&a[i]!=fail)
			 {
				 book[i]=1;//记录使用这个棍
				if( dfs(cnt,cur+a[i],i+1))   return true;//能放,就直接走了
				 book[i]=0;
				 fail=a[i];//记录失败的木棍
				 if(cur==0||cur+a[i]==len) return false;
			 }//剪枝 如果是第一个就失败或者最后一个失败,该情况一定不符,直接排除
		 }
		return false;		 
	 }
	}


package dfs;
import java.util.ArrayList;
	import java.util.Arrays;
	import java.util.Comparator;
	import java.util.Scanner;
public class 小木棍 {
//改bug思考,仔细想一下为啥不去等1234的数据会过不去呢???//明白了x每次从1开始,第0个读取不到
		//思路 按从大到小排序//剪枝 先搜情况少的、
		//如果i木棍失败了 ,后面遇到了与i等长的j木棍,则 直接判定失败
		//如果第一根就失败了,那么直接判定此情况失败、//同理,最后一个失败,则也失败
		static Integer a[]=new Integer [70];
		static int len;
		static int n;
		static int num;
		static int sum;
		static int book[]=new int [70];
	 public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		 n=sc.nextInt();
		 
		int max=0;
		
		for(int i=1;i<=n;i++)//之前从0开始读n不带等,会数组越界
		{
			a[i]=sc.nextInt();
			sum+=a[i];
			max=Math.max(max, a[i]);
		}//剪枝 先对可选择情况少的(大木棍)进行拼接
		Arrays.sort(a,1,n,new Comparator<Integer>() {

			@Override
			public int compare(Integer o1, Integer o2) {
				// TODO Auto-generated method stub
				return o2-o1;
			}
		});
		for( len=max;len<=sum;len++)
		{
			if(sum%len!=0) continue;//剪枝 因为符合题意的长度一定是sum的因子
		  num=sum/len;//当前长度下的拼接好的木棍个数
		  if(dfs(1,0,1)) break;
		}
		System.out.println(len);
		
	}
	 //cnt 现在正在处理的木棒
	 //cur 现在的木棒长度
	 //x 当前正在拼的木棍的第一个可用的编号//为了保证木棍组成木棒的编号 是递减的,避免重复
	 static boolean dfs(int cnt,int cur,int x)
	 {
		 //System.out.println(cnt+" "+cur);
		 if(cnt>=num) return true;
		 if(cur==len) return dfs(cnt+1,0,1);
		 //剪枝 记录已经失败的木棍的长度
		 int fail=0;
		 
		 for(int i=x;i<=n;i++)//这个地方一定要有等号,不然会遍历不全
		 {
			 if(book[i]==0&&cur+a[i]<=len&&a[i]!=fail)
			 {
				 book[i]=1;//记录使用这个棍
				if( dfs(cnt,cur+a[i],i+1))   return true;//能放,就直接走了
				 book[i]=0;
				 fail=a[i];//记录失败的木棍
				 if(cur==0||cur+a[i]==len) return false;
			 }//剪枝 如果是第一个就失败或者最后一个失败,该情况一定不符,直接排除
		 }
		return false;		 
	 }
	}

不过要注意的是,这个代码并不能通过洛谷的全部数据(超时){java太慢了?} 博主后期还会进一步优化哒~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值