动态规划——割绳子问题

问题如下:
Give you a rope of length n, please cut the rope into m segments (m and n are integers, n>1 and m>1). The length of each segment is denoted by k[0], k[1], …, k[m]. What is the maximum possible product of k[0]k[1]…*k[m]? And analyze the time complexity of your solution
翻译:
给你一根长度为 n 的绳子,请把绳子切成 m 段(m 和 n 是整数,n > 1和 m > 1)。每个段的长度用 k [0] ,k [1] ,… ,k [ m ]表示。K [0] k [1] … k [ m ]的最大可能乘积是多少?并分析解决方案的时间复杂性
注意至少割一刀
解决思路和方案如下:

	用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
	/*求n长度绳子至少剪一刀的最大乘积,要由比n小的这么一个长度的绳子得来
	 * 对一个n长度的绳子,其子问题的长度范围是0-n,代表0长度到n长度的绳子,我们用i来表示这些长度,i的范围是[0,n]
	 * 因为至少剪一刀,长度至少为1,但这个1,对最后的乘积没有任何作用,白白消耗了长度1的绳子
	 * 所以我们从长度为2开始剪,设剪下的这一段长度为j,j的范围是[2,i-1],剪完一刀的长度不可能是i,那就等同于没剪
	 * 当剪下一段长度j的绳子后,后面还有i-j长度的绳子,有两种选择,要么不剪了乘积是i*(i-j),要么对后面i-j长度的绳子再剪至少一刀,那么长度是i*dp[i-j]
	 * 我们取更大值max(i*(i-j),i*dp[i-j]),但这只是对于一个长度为i的绳子,其中的某一种剪法得来的当前较大值。
	 * 比如说长度i为5,j从2开始,我们得到的只是长度为5的绳子,剪成2和3,或者剪成2,2,1(最dp[3]展开后是2,1)这两种方案的较大值是6.dp[5]暂记为6
	 * 但还有j从3开始的,j从4开始的那些方案,所以目前这个6只是长度为5的绳子,枚举出的方案之一,我们要的是:这些方案中哪一个使dp[5]最大
	 * 所以这个dp[5]=6,只是当前较大值,后面还需要更新,尽管后续方案可能都比不上这个6,但我们还需要比较,因为那是长度很特殊,比如这个长度5
	 * j从2开始,执行完后dp[5]是6
	 * j从3开始,max(3*2,3*dp[2]),是6
	 * j从4开始,max(4*1,4*dp[1]),是4。这里dp[1]=0,1长度还要求至少剪一刀,没法剪,根本不满足题意要求,我们把他当做0,当做null更好理解,但这里计算可能会出问题,我没有尝试
	 * 所以我们在j从2开始那个方案中求得的6,就是最后的最大值了,但也有特殊的,大家可以自己举一下例子。
	 */
import java.util.Scanner;

public class lab2_1 {
	public static int findmax(int n)
	{
		int[] dp=new int[n+1];
		dp[0]=dp[1]=0;
		dp[2]=1;
		for(int i=3;i<=n;i++)
		{
			for(int j=2;j<i;j++)
			{
				dp[i]=Math.max( dp[i], Math.max(  j*(i-j),j*dp[i-j]  )       );
			}
		}
		return dp[n];
			
	}
	public static void main(String[] args) {
		// TODO 自动生成的方法存根
		//用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积,也就是dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1
		/*求n长度绳子至少剪一刀的最大乘积,要由比n小的这么一个长度的绳子得来
		 * 对一个n长度的绳子,其子问题的长度范围是0-n,代表0长度到n长度的绳子,我们用i来表示这些长度,i的范围是[0,n]
		 * 因为至少剪一刀,长度至少为1,但这个1,对最后的乘积没有任何作用,白白消耗了长度1的绳子
		 * 所以我们从长度为2开始剪,设剪下的这一段长度为j,j的范围是[2,i-1],剪完一刀的长度不可能是i,那就等同于没剪
		 * 当剪下一段长度j的绳子后,后面还有i-j长度的绳子,有两种选择,要么不剪了乘积是i*(i-j),要么对后面i-j长度的绳子再剪至少一刀,那么长度是i*dp[i-j]
		 * 我们取更大值max(i*(i-j),i*dp[i-j]),但这只是对于一个长度为i的绳子,其中的某一种剪法得来的当前较大值。
		 * 比如说长度i为5,j从2开始,我们得到的只是长度为5的绳子,剪成2和3,或者剪成2,2,1(最dp[3]展开后是2,1)这两种方案的较大值是6.dp[5]暂记为6
		 * 但还有j从3开始的,j从4开始的那些方案,所以目前这个6只是长度为5的绳子,枚举出的方案之一,我们要的是:这些方案中哪一个使dp[5]最大
		 * 所以这个dp[5]=6,只是当前较大值,后面还需要更新,尽管后续方案可能都比不上这个6,但我们还需要比较,因为那是长度很特殊,比如这个长度5
		 * j从2开始,执行完后dp[5]是6
		 * j从3开始,max(3*2,3*dp[2]),是6
		 * j从4开始,max(4*1,4*dp[1]),是4。这里dp[1]=0,1长度还要求至少剪一刀,没法剪,根本不满足题意要求,我们把他当做0,当做null更好理解,但这里计算可能会出问题,我没有尝试
		 * 所以我们在j从2开始那个方案中求得的6,就是最后的最大值了,但也有特殊的,大家可以自己举一下例子。
		 */
		Scanner sc =new Scanner(System.in);
		System.out.println("请输入绳子的长度n");
		int n=sc.nextInt();
		int result=findmax(n);
		System.out.println("绳子分成若干段后的最大乘积是"+result);	
	}

}

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
s': // 地球向下移动 earth_pos[1] -= 0.1; break; } } // 定时器事件处理函数 void timer(int value) { update(); glutTimerFunc(20, timer, 0); } 绳子是一款非常流行的益智游戏,它的核心玩法是通过切绳子来// 主函数 int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT让物体掉落到指定位置,同时需要收集星星来获取更高的分数。在 Cocos Creator 中_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("Earth and Moon"); glut实现绳子的过程大致可以分为以下几个步骤: 1. 创建游戏场景和物体DisplayFunc(display); glutMouseFunc(mouse); glutKeyboardFunc(keyboard); glutTimerFunc(20, timer, 0); :使用 Cocos Creator 的场景编辑器创建游戏场景,然后添加需要切绳子和物体等 init(); glutMainLoop(); return 0; } ``` 这个程序使用了OpenGL库,实现了一个简游戏元素。 2. 实现物体的掉落:使用物理引擎(如 Cocos Creator 自带的 Box单的地月绕太阳公转的效果。其中地球和月球是用图素构造法构成的2D 物理引擎)来实现物体的掉落,同时设置物体的刚体属性、碰撞体属性三维球体,太阳是一个实心球体。程序支持鼠标左键点击开始运动,支持键等。 3. 实现绳子的切:在游戏场景中添加触摸事件监听器,当玩家触盘控制地球改变轨道。此外,地球和月球还添加了phong光照效果,增摸到绳子时,计算出触摸点与绳子的交点,并将绳子分成两段。可以使用强了真实感。 程序中使用了三个数组分别存储地球和月球的位置和速度,每 Cocos Creator 自带的 Graphics 组件来绘制切后的绳子。 4. 实现星星的收集:在次更新时根据万有引力定律计算它们的位置和速度。鼠标事件处理函数中实现游戏场景中添加触发器,当物体与星星重叠时,触发收集星星的事件,并了视角的变换,键盘事件处理函数中实现了地球的移动。定时器事件处理函数中更新分数。 5. 实现游戏结束逻辑:当物体掉落到指定位置或者掉落到屏不断更新地球和月球的位置和速度,并进行重绘。 希望这个示范程序对你有所帮助。如果还有疑问,可以继续提问。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值