Knapsack I 竟然是贪心,证明啊。。。。

本文探讨了一种特定条件下的背包问题,即所有物品价值之间存在整除关系时的解决方案。通过对输入数值进行排序并采用贪心策略,文章提供了一个简单而有效的算法实现,用于判断是否能从给定的数值集合中选取若干个数使其和等于目标值。

Knapsack I

Time Limit:  2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)
Submit Status
Problem Description
给出N个数a[1]....a[N],对于任意两个数(a[i],a[j]),保证a[i] % a[j] == 0 || a[j] % a[i] == 0,再给出一个数M,问这些数能否取出若干个来,使得其和为M
Input

多组数据,每组数组首先输入N,然后接下来有N个数a[1] ... a[N],然后输入M,含义如题目描述

1 <= N <= 24, 1 <= a[i],M <= 10000000

Output
每组数据,输出一行,如果可以组成M,输出"YES",否则输出"NO".
Sample Input
4
1 1 1 1 
3
3
1 3 9
11
Sample Output
YES
NO

直接贪心。就是现实生活中的找零钱,将输入从大到小排序, for(int i = 1; i <= n; i ++)
if(m >= a[i]) m -= a[i];
if(!m) YES; else NO
证明:不妨设存在合法方案 m = c1 + c2 + .. ck(c1 <= c2 <= ... <= ck)
假设你现在要用一个更大的数 X 去取代部分数
求出最小的 i,使得 c1 + c2 + ... + ci >= X,c1 + c2 + ... + c[i - 1] < X
尝试去掉 c1,如果 c2 + ... + ci >= X,最后剩下的串为 cj + ... + ci >= X,且 c[j + 1] + .. + ci < X,(否则继续去掉 cj)
如果 cj > X,那么 X 一定不能被替换了。
否则,因为 cj + 1 % cj = 0.. c[i] % c[i - 1] == 0,且 X >= c[j], X % c[j] = 0
设 cj + .. + ci = k1 * cj  X = k2 * cj,  cj + 1 + .. + ci = k3 * cj
k1 >= k2 > k3,
所以 k2 >= k3 + 1 因为 k1 - k3 == 1,只差一个 cj
所以 k2 = k1,因此可以直接用 X 替换掉 cj + .. + ci

 

 1 #include <iostream>
 2 #include <stdio.h>
 3 #include <algorithm>
 4 #include <string.h>
 5 using namespace std;
 6 
 7 int main()
 8 {
 9     int n,m,i,j,a[30];
10     while(scanf("%d",&n)!=EOF)
11     {
12         for(i=0;i<n;i++)scanf("%d",&a[i]);
13         scanf("%d",&m);
14         sort(a,a+n);
15         for(i=n-1;i>=0;i--)
16         {
17             if(m>=a[i])
18             m-=a[i];
19         }
20         if(m)
21         printf("NO\n");
22         else printf("YES\n");
23     }
24 
25 }
View Code

 

转载于:https://www.cnblogs.com/ERKE/p/3852761.html

<think>好的,我现在需要回答用户关于贪心算法示例代码和实现方法的问题。首先,我得回忆一下贪心算法的基本概念,以及常见的应用场景。用户可能希望看到具体的代码示例,比如在LeetCode上的题目,或者一些经典问题如背包问题、活动选择问题等。根据用户提供的引用内容,他们提到了几个例子,比如摆动序列、单调递增的数字、区间划分和种花问题。这些都可以作为示例代码的来源。例如,引用[2]中的摆动序列问题使用了贪心策略,通过交替选择上升和下降的最长序列。引用[3]中的装载问题,可能是指船运货物,使用贪心策略将轻的和重的配对。引用[4]中的区间划分,将字符串划分为尽可能多的片段,每个字母只出现在一个片段中。引用[5]中的种花问题,每一步尽可能种花,确保局部最优。 接下来,我需要选择几个典型的例子,给出代码实现,并解释其贪心策略。需要注意代码的格式是否正确,比如Python和Java的不同语法。同时,要确保说明每个示例的贪心思想,让用户明白为什么这些情况下贪心算法有效,以及如何证明其正确性。另外,用户要求生成相关问题,我需要考虑用户可能进一步想知道的内容,比如贪心算法的适用条件、如何证明正确性、与其他算法的比较等。这样能帮助用户更全面地理解贪心算法。 最后,检查引用是否正确,引用的内容是否与示例相关,比如引用[2]对应摆动序列,引用[4]对应字符串划分。确保每个代码示例后都有对应的引用标注,并且格式正确。</think>### 贪心算法示例代码与实现方法 贪心算法通过**局部最优选择**逐步逼近全局最优解,适用于满足贪心选择性质的问题。以下是几个典型示例及实现: --- #### 示例1:摆动子序列(LeetCode 376) **问题描述**:求整数数组中最长摆动子序列的长度(相邻元素差正负交替)。 ```python def wiggleMaxLength(nums): if len(nums) < 2: return len(nums) up = down = 1 for i in range(1, len(nums)): if nums[i] > nums[i-1]: up = down + 1 elif nums[i] < nums[i-1]: down = up + 1 return max(up, down) ``` **贪心策略**:交替记录上升/下降趋势的长度,每次只关注当前状态的最优解[^2]。 --- #### 示例2:区间划分(LeetCode 763) **问题描述**:将字符串划分为尽可能多的片段,使每个字符只出现在一个片段中。 ```python def partition_labels(s): last_index = {c: i for i, c in enumerate(s)} start = end = 0 res = [] for i, c in enumerate(s): end = max(end, last_index[c]) if i == end: res.append(end - start + 1) start = i + 1 return res ``` **贪心策略**:记录字符最后出现的位置,动态扩展当前区间的终点[^4]。 --- #### 示例3:种花问题(LeetCode 605) **问题描述**:在花坛中能否种n朵花,要求相邻地块不能同时种。 ```python def can_place_flowers(flowerbed, n): count = 0 i = 0 while i < len(flowerbed): if flowerbed[i] == 0 and (i == 0 or flowerbed[i-1] == 0) and (i == len(flowerbed)-1 or flowerbed[i+1] == 0): flowerbed[i] = 1 count += 1 i += 2 # 跳过下一块 else: i += 1 return count >= n ``` **贪心策略**:遇到可种植位置立即种花,跳过相邻地块[^5]。 --- #### 示例4:背包问题(分数版) **问题描述**:在容量限制下选择价值最高的物品(允许取部分物品)。 ```python def fractional_knapsack(values, weights, capacity): items = sorted(zip(values, weights), key=lambda x: x[0]/x[1], reverse=True) total = 0.0 for v, w in items: if capacity >= w: total += v capacity -= w else: total += v * (capacity / w) break return total ``` **贪心策略**:优先选择单位价值最高的物品[^1]。 --- ### 贪心算法实现要点 1. **问题分析**:确认问题是否满足贪心选择性质(局部最优导致全局最优)。 2. **策略设计**:明确每一步如何做出局部最优选择。 3. **正确性证明**:通常使用数学归纳法或反证法验证策略有效性。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值