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

Knapsack I

Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others)
SubmitStatus

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
");
22         else printf("YES
");
23     }
24 
25 }
View Code
原文地址:https://www.cnblogs.com/ERKE/p/3852761.html