DP11 高楼扔鸡蛋问题 Egg Dropping Puzzle @geeksforgeeks

本文探讨了经典的两蛋百层楼问题,旨在通过最少次数的试验找出从哪一层开始投掷鸡蛋会破碎的楼层。文章提供了两种解决方案,一种是递归方法,另一种是动态规划方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 

100层楼,两个鸡蛋。某层之上扔鸡蛋就会碎。问至少要测试多少次才能找出这层楼来?


思路:
首先要理解清楚题意,本题不是要找在哪一层以上会把鸡蛋扔破!而是我们假设在W层以上会把鸡蛋仍破,现在问至少要测试Y次才能找到这个W层?求的是Y
另外一个要注意的是本题中要基于扔的人运气最差的情况,即要保证在worst case下也能找到。
定义W层位蛋破层,则
我们第一次从第x层扔蛋下去,有两种可能:
1) 蛋破了:这说明了两点 1.我们手上仍然有n-1颗蛋 2.蛋破层一定在1...x-1之间,这里共有x-1-1+1 =>x-1数量的嫌疑楼层
2)蛋没破:这也说明了两点 1.我们手上仍然有n颗蛋 2.蛋破层一定在x+1...k之间,这里共有k-(x+1)+1 =>k-x数量的嫌疑楼层

定义eggDrop(n, k)为在最差情况下找出蛋破层所需要的最少扔数。n为蛋的数量,k为要检查的连续的楼层的数量
因为我们要基于最差情况,所以我们要选择两种可能之间更坏的那种,即导致扔的数量更多的那种情况,
max(eggDrop(n - 1, x - 1), eggDrop(n, k - x))
然后我们遍历所有楼层,要找出能使得最后扔的数量最小的那个初始楼层,因此
eggDrop(n, k) = 1 + min{max(eggDrop(n - 1, x - 1), eggDrop(n, k - x)): x in {1, 2, ..., k}}
加1是因为在第x层测试时也消耗了1次。

 

package DP;

/**
 * 100层楼,两个鸡蛋。某层之上扔鸡蛋就会碎。问至少要测试多少次才能找出这层楼来
 * 我们可以决定怎么扔(min),但必须假设我们的运气最差(max)
 */
public class EggDroppingPuzzle {

	public static void main(String[] args) {
		int n = 2, k = 20;
		System.out.println(eggDropDP(n, k));
		System.out.println(eggDropRec(n, k));
	}
	
	/* Function to get minimum number of trails needed in worst
	  case with n eggs and k floors */
	// n: 手上完好蛋的数量
	// k: 要测试的连续楼层的数量
	public static int eggDropRec(int n, int k){
		// If there are no floors, then no trials needed. OR if there is
	    // one floor, one trial needed.
		if(k==1 || k==0){
			return k;
		}
		// We need k trials for one egg and k floors
		if(n == 1){
			return k;
		}
		int min = Integer.MAX_VALUE;
		
		/**
		 我们要决策怎么扔使得总扔数最少(即使在我们运气最差的情况下)worst-case
		 所以在每一楼都测试第一扔,然后递归计算出总共需要的扔数,找到扔数最小的数量
		 所以假设在第x层扔第一个蛋:
		 结果1:蛋破了->现在我们只有n-1个蛋,还要测试x-1数量的楼层[1,x-1] -> 数量=x-1 -1 + 1 = x-1
		 结果2: 但没破 -> 现在我们仍有n个蛋,还要测试k-x数量的楼层[x+1,k] -> 数量=k - (x+1) + 1 = k-x
		 */
		for(int x=1; x<=k; x++){
			int res = Math.max(eggDropRec(n-1, x-1), eggDropRec(n, k-x));
			min = Math.min(min, res);
		}
		return min+1;		// +1是因为测试当前层消耗了一次扔
	}

	/* Function to get minimum number of trails needed in worst
	  case with n eggs and k floors */
	// Time Complexity: O(nk^2), Auxiliary Space: O(nk)
	public static int eggDropDP(int n, int k){
		/* A 2D table where entery dp[i][j] will represent minimum
	       number of trials needed for i eggs and j floors. */
		int[][] dp = new int[n+1][k+1];	
		
		// We need one trial for one floor and0 trials for 0 floors
		for(int i=1; i<=n; i++){
			dp[i][1] = 1;
			dp[i][0] = 0;
		}
		// We always need j trials for one egg and j floors.
		for(int j=1; j<=k; j++){
			dp[1][j] = j;
		}
		// Fill rest of the entries in table using optimal substructure
	    // property
		for(int i=2; i<=n; i++){			// i eggs
			for(int j=2; j<=k; j++){	// j floors
				dp[i][j] = Integer.MAX_VALUE;
				for(int x=1; x<=j; x++){	// try every floor from 1...j
					int res = 1 + Math.max(dp[i-1][x-1], dp[i][j-x]);
					dp[i][j] = Math.min(dp[i][j], res);
				}
			}
		}
		return dp[n][k];
	}
}

  

转载于:https://www.cnblogs.com/AlgoWing/p/3491677.html

<think>好的,我现在需要解决用户提到的Android Activity因无窗口焦点而丢弃事件的问题。首先,我需要理解问题的具体情况。当Activity失去窗口焦点时,比如用户跳转到另一个应用或弹出对话框,系统可能会丢弃该Activity的输入事件,导致无法及时处理用户操作。 接下来,我应该回忆Android的事件处理机制。窗口焦点管理是关键,当Activity不在前台时,它的窗口会失去焦点,系统可能停止向其发送事件。用户可能需要在这些情况下仍然处理某些事件,比如游戏中的后台处理或媒体控制。 首先想到的是检查窗口焦点状态,并确保在适当的时候恢复处理。可能的解决方案包括: 1. **焦点监听**:在Activity中添加OnWindowFocusChangeListener,这样可以在焦点变化时做出响应。当焦点恢复时,重新启用事件处理;失去焦点时,暂停或保存状态。这可以防止在无焦点时处理事件导致的错误。 2. **保持焦点**:使用View的keepScreenOn属性或者WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON来保持屏幕常亮,但这可能更多是防止屏幕关闭,不一定解决焦点丢失的问题。需要进一步确认。 3. **服务处理**:对于需要在后台持续处理的事件,可以考虑使用Service,尤其是前台服务,这样即使Activity不在前台,服务仍能运行。但事件处理通常与UI相关,服务可能不是直接解决方案。 4. **事件重定向**:如果另一个视图获得了焦点,可能需要将事件传递给正确的组件。比如在自定义View中重写onTouchEvent,确保事件不被父视图拦截。 然后,需要验证这些方法的正确性。比如,焦点监听是标准的做法,代码示例是否正确?比如在Activity的onCreate中添加: ```java getWindow().getDecorView().addOnWindowFocusChangeListener(hasFocus -> { if (hasFocus) { // 恢复事件处理 } else { // 暂停或保存状态 } }); ``` 另外,用户可能需要处理特定的输入事件,比如触摸或按键。对于触摸事件,确保在View层级中没有被意外拦截,检查onInterceptTouchEvent是否正确处理。如果使用SurfaceView,需要注意其渲染线程的生命周期是否与Surface的创建/销毁绑定,如引用[1]提到的,可能需要根据情况选择启动/停止线程的方式。 此外,可能涉及输入事件队列的管理。当Activity失去焦点时,系统可能清除事件队列,导致后续事件无法处理。此时,需要在失去焦点时保存必要状态,并在恢复时重新初始化。 最后,综合这些可能的解决方案,给出具体的步骤和代码示例,确保用户能够根据实际情况调整。同时,生成相关问题,帮助用户深入理解相关机制,比如焦点管理、事件分发流程等。</think>针对Android中Activity因无窗口焦点导致事件丢失的问题,可通过以下方案实现稳定事件处理: ### 一、焦点状态监听与控制 1. 在`Activity`中注册窗口焦点监听器,实时处理焦点变化: ```java @Override public void onWindowFocusChanged(boolean hasFocus) { super.onWindowFocusChanged(hasFocus); if (hasFocus) { // 恢复事件处理逻辑 resumeEventProcessing(); } else { // 暂停敏感操作并保存状态 pauseEventProcessing(); } } ``` 2. 主动请求焦点保持(适用于游戏/视频类应用): ```java // 在onCreate()中添加 getWindow().setFlags( WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL ); ``` ### 二、事件分发优化 1. 在自定义View中强制获取焦点: ```xml <CustomView android:focusable="true" android:focusableInTouchMode="true"/> ``` 2. 重写事件处理方法(适用于SurfaceView/TextureView): ```java @Override public boolean onTouchEvent(MotionEvent event) { if (!hasWindowFocus()) { // 执行焦点恢复策略 requestFocus(); return true; } return super.onTouchEvent(event); } ``` ### 三、后台事件处理策略 对于必须后台处理的事件(如媒体控制),建议采用组合方案: 1. 前台服务保活: ```java // 创建前台服务通知 Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("Event Processor") .setSmallIcon(R.drawable.ic_notification) .build(); startForeground(1, notification); ``` 2. 输入事件代理机制: ```java InputManager inputManager = (InputManager) getSystemService(INPUT_SERVICE); inputManager.registerInputDeviceListener(...); ``` ### 四、SurfaceView线程管理 根据渲染线程的生命周期需求选择绑定策略[^1]: - **绑定Activity生命周期**:适用于需要即时响应的场景 - **绑定Surface生命周期**:适用于需要持续渲染的场景 ```java // SurfaceHolder.Callback实现 public void surfaceCreated(SurfaceHolder holder) { renderThread = new RenderThread(holder); renderThread.start(); } public void surfaceDestroyed(SurfaceHolder holder) { renderThread.quitSafely(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值