puzzle(1242)单人决策问题

目录

倒水问题

力扣 365. 水壶问题

兑换问题

力扣 1518. 换水问题

鸡蛋楼层问题

力扣 1884. 鸡蛋掉落-两枚鸡蛋

力扣 887. 鸡蛋掉落

点灯问题

力扣 995. K 连续位的最小翻转次数

分辨毒药问题

力扣 458. 可怜的小猪


倒水问题

力扣 365. 水壶问题

有两个水壶,容量分别为 jug1Capacity 和 jug2Capacity 升。水的供应是无限的。确定是否有可能使用这两个壶准确得到 targetCapacity 升。

如果可以得到 targetCapacity 升水,最后请用以上水壶中的一或两个来盛放取得的 targetCapacity 升水。

你可以:

装满任意一个水壶
清空任意一个水壶
从一个水壶向另外一个水壶倒水,直到装满或者倒空
 

示例 1: 

输入: jug1Capacity = 3, jug2Capacity = 5, targetCapacity = 4
输出: true
解释:来自著名的 "Die Hard"
示例 2:

输入: jug1Capacity = 2, jug2Capacity = 6, targetCapacity = 5
输出: false
示例 3:

输入: jug1Capacity = 1, jug2Capacity = 2, targetCapacity = 3
输出: true
 

提示:

1 <= jug1Capacity, jug2Capacity, targetCapacity <= 106

long long gcd(long long a, long long b)
{
	if (b == 0)return a;
	return gcd(b, a%b);
}
class Solution {
public:
	bool canMeasureWater(int a, int b, int s) {
		if (s > a + b)return false;
		int c = gcd(a, b);
		return s % c == 0;
	}
};

兑换问题

兑换问题分为最后可以借1个空瓶子,和不能借,2种版本。

2种版本的答案可能一样也可能隔1。

力扣 1518. 换水问题

超市正在促销,你可以用 numExchange 个空水瓶从超市兑换一瓶水。最开始,你一共购入了 numBottles 瓶水。

如果喝掉了水瓶中的水,那么水瓶就会变成空的。

给你两个整数 numBottles 和 numExchange ,返回你 最多 可以喝到多少瓶水。

示例 1:

输入:numBottles = 9, numExchange = 3
输出:13
解释:你可以用 3 个空瓶兑换 1 瓶水。
所以最多能喝到 9 + 3 + 1 = 13 瓶水。

示例 2:

输入:numBottles = 15, numExchange = 4
输出:19
解释:你可以用 4 个空瓶兑换 1 瓶水。
所以最多能喝到 15 + 3 + 1 = 19 瓶水。

提示:

  • 1 <= numBottles <= 100
  • 2 <= numExchange <= 100
class Solution {
public:
    int numWaterBottles(int numBottles, int numExchange) {
        return numBottles*numExchange/(numExchange-1)-(numBottles%(numExchange-1)==0);
    }
};

鸡蛋楼层问题

力扣 1884. 鸡蛋掉落-两枚鸡蛋

给你 2 枚相同 的鸡蛋,和一栋从第 1 层到第 n 层共有 n 层楼的建筑。

已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都 会碎 ,从 f 楼层或比它低 的楼层落下的鸡蛋都 不会碎 

每次操作,你可以取一枚 没有碎 的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?

示例 1:

输入:n = 2
输出:2
解释:我们可以将第一枚鸡蛋从 1 楼扔下,然后将第二枚从 2 楼扔下。
如果第一枚鸡蛋碎了,可知 f = 0;
如果第二枚鸡蛋碎了,但第一枚没碎,可知 f = 1;
否则,当两个鸡蛋都没碎时,可知 f = 2。

示例 2:

输入:n = 100
输出:14
解释:
一种最优的策略是:
- 将第一枚鸡蛋从 9 楼扔下。如果碎了,那么 f 在 0 和 8 之间。将第二枚从 1 楼扔下,然后每扔一次上一层楼,在 8 次内找到 f 。总操作次数 = 1 + 8 = 9 。
- 如果第一枚鸡蛋没有碎,那么再把第一枚鸡蛋从 22 层扔下。如果碎了,那么 f 在 9 和 21 之间。将第二枚鸡蛋从 10 楼扔下,然后每扔一次上一层楼,在 12 次内找到 f 。总操作次数 = 2 + 12 = 14 。
- 如果第一枚鸡蛋没有再次碎掉,则按照类似的方法从 34, 45, 55, 64, 72, 79, 85, 90, 94, 97, 99 和 100 楼分别扔下第一枚鸡蛋。
不管结果如何,最多需要扔 14 次来确定 f 。

提示:

  • 1 <= n <= 1000

思路:dp

f(n)=min{ max(f(n-k)+1,k) | 1<=k<=n}

class Solution {
public:
	int twoEggDrop(int n) {
		return sqrt(n * 2 + 0.25) + 0.4999;
	}
};

力扣 887. 鸡蛋掉落

给你 k 枚相同的鸡蛋,并可以使用一栋从第 1 层到第 n 层共有 n 层楼的建筑。

已知存在楼层 f ,满足 0 <= f <= n ,任何从 高于 f 的楼层落下的鸡蛋都会碎,从 f 楼层或比它低的楼层落下的鸡蛋都不会破。

每次操作,你可以取一枚没有碎的鸡蛋并把它从任一楼层 x 扔下(满足 1 <= x <= n)。如果鸡蛋碎了,你就不能再次使用它。如果某枚鸡蛋扔下后没有摔碎,则可以在之后的操作中 重复使用 这枚鸡蛋。

请你计算并返回要确定 f 确切的值 的 最小操作次数 是多少?

 
示例 1:

输入:k = 1, n = 2
输出:2
解释:
鸡蛋从 1 楼掉落。如果它碎了,肯定能得出 f = 0 。 
否则,鸡蛋从 2 楼掉落。如果它碎了,肯定能得出 f = 1 。 
如果它没碎,那么肯定能得出 f = 2 。 
因此,在最坏的情况下我们需要移动 2 次以确定 f 是多少。 
示例 2:

输入:k = 2, n = 6
输出:3
示例 3:

输入:k = 3, n = 14
输出:4
 

提示:

1 <= k <= 100
1 <= n <= 104

代码:

class Solution {
public:
    int ans[101][10001];
    Solution(){
        for(int i=0;i<101;i++)for(int j=0;j<10001;j++)ans[i][j]=0;
    }
    int superEggDrop(int k, int n) {
        if(ans[k][n])return ans[k][n];
        if(k==1)return n;
        if(n==0)return 0;
        ans[k][n]=n;
        int low=1,high=n;
        while(high-low>5){
            int m=(low+high)/2;
            if(superEggDrop(k-1,m-1)>superEggDrop(k,n-m))high=m;
            else low=m;
        }
        for(int i=low;i<=high;i++)ans[k][n]=min(ans[k][n],1+max(superEggDrop(k-1,i-1),superEggDrop(k,n-i)));
        return ans[k][n];
    }
};

PS:

需要用二分才能降低时间复杂度,而且不能用三分

点灯问题

一维:

力扣 995. K 连续位的最小翻转次数

给定一个二进制数组 nums 和一个整数 k 。

k位翻转 就是从 nums 中选择一个长度为 k 的 子数组 ,同时把子数组中的每一个 0 都改成 1 ,把子数组中的每一个 1 都改成 0 。

返回数组中不存在 0 所需的最小 k位翻转 次数。如果不可能,则返回 -1 。

子数组 是数组的 连续 部分。

示例 1:

输入:nums = [0,1,0], K = 1
输出:2
解释:先翻转 A[0],然后翻转 A[2]。

示例 2:

输入:nums = [1,1,0], K = 2
输出:-1
解释:无论我们怎样翻转大小为 2 的子数组,我们都不能使数组变为 [1,1,1]。

示例 3:

输入:nums = [0,0,0,1,0,1,1,0], K = 3
输出:3
解释:
翻转 A[0],A[1],A[2]: A变成 [1,1,1,1,0,1,1,0]
翻转 A[4],A[5],A[6]: A变成 [1,1,1,1,1,0,0,0]
翻转 A[5],A[6],A[7]: A变成 [1,1,1,1,1,1,1,1]

提示:

  • 1 <= nums.length <= 105
  • 1 <= k <= nums.length
class Solution {
public:
	int minKBitFlips(vector<int>& nums, int k) {
		vector<int>ans(nums.size(), 0);
		int s = ans[0] = 1 - (nums[0] & 1);
		for (int i = 1; i < ans.size(); i++) {
			ans[i] = nums[i] + nums[i - 1];
			if (i >= k)ans[i] += ans[i - k];
			s += ans[i] & 1;
			if (i > ans.size() - k && (ans[i] & 1))return -1;
		}
		return s;
	}
};

二维及更多变化:

点亮所有的灯

分辨毒药问题

力扣 458. 可怜的小猪

buckets 桶液体,其中 正好有一桶 含有毒药,其余装的都是水。它们从外观看起来都一样。为了弄清楚哪只水桶含有毒药,你可以喂一些猪喝,通过观察猪是否会死进行判断。不幸的是,你只有 minutesToTest 分钟时间来确定哪桶液体是有毒的。

喂猪的规则如下:

  1. 选择若干活猪进行喂养
  2. 可以允许小猪同时饮用任意数量的桶中的水,并且该过程不需要时间。
  3. 小猪喝完水后,必须有 minutesToDie 分钟的冷却时间。在这段时间里,你只能观察,而不允许继续喂猪。
  4. 过了 minutesToDie 分钟后,所有喝到毒药的猪都会死去,其他所有猪都会活下来。
  5. 重复这一过程,直到时间用完。

给你桶的数目 buckets ,minutesToDie 和 minutesToTest ,返回 在规定时间内判断哪个桶有毒所需的 最小 猪数 。

示例 1:

输入:buckets = 1000, minutesToDie = 15, minutesToTest = 60
输出:5

示例 2:

输入:buckets = 4, minutesToDie = 15, minutesToTest = 15
输出:2

示例 3:

输入:buckets = 4, minutesToDie = 15, minutesToTest = 30
输出:2

提示:

  • 1 <= buckets <= 1000
  • 1 <= minutesToDie <= minutesToTest <= 100

思路:

每只猪可以试错的次数是t=minutesToTest/minutesToDie

当t=1时,每只猪可以提供2种状态,利用二进制对buckets进行编号,每只猪只需要确定一位即可。所以需要的猪的数量是log_2n

推广到一般情况,答案就是log_{t+1}n

class Solution {
public:
	int poorPigs(int buckets, int minutesToDie, int minutesToTest) {
		int t = minutesToTest / minutesToDie + 1;
		int ans = 0, s = 1;
		while (s < buckets)s *= t, ans++;
		return ans;
	}
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值