Knapsack problem(背包问题)
The knapsack problem or rucksack problem is a problem in combinatorial optimization: Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than a given limit and the total value is as large as possible. It derives its name from the problem faced by someone who is constrained by a fixed-size knapsack and must fill it with the most useful items.
The problem often arises in resource allocation with financial constraints. A similar problem also appears in combinatorics, complexity theory, cryptography and applied mathematics.
The decision problem form of the knapsack problem is the question "can a value of at least V be achieved without exceeding the weight W?"
This is a dynamic-programming algorithm implementation for solving the the 0-1 Knapsack Problem in C.
Further explanation is given here.

#include <stdio.h>
#define MAXWEIGHT 100
int n = 3; /* The number of objects */
int c[10] = {8, 6, 4}; /* c[i] is the *COST* of the ith object; i.e. what
YOU PAY to take the object */
int v[10] = {16, 10, 7}; /* v[i] is the *VALUE* of the ith object; i.e.
what YOU GET for taking the object */
int W = 10; /* The maximum weight you can take */
void fill_sack() {
int a[MAXWEIGHT]; /* a[i] holds the maximum value that can be obtained
using at most i weight */
int last_added[MAXWEIGHT]; /* I use this to calculate which object were
added */
int i, j;
int aux;
for (i = 0; i <= W; ++i) {
a[i] = 0;
last_added[i] = -1;
}
a[0] = 0;
for (i = 1; i <= W; ++i)
for (j = 0; j < n; ++j)
if ((c[j] <= i) && (a[i] < a[i - c[j]] + v[j])) {
a[i] = a[i - c[j]] + v[j];
last_added[i] = j;
}
for (i = 0; i <= W; ++i)
if (last_added[i] != -1)
printf("Weight %d; Benefit: %d; To reach this weight I added object %d (%d$ %dKg) to weight %d.\n", i, a[i], last_added[i] + 1, v[last_added[i]], c[last_added[i]], i - c[last_added[i]]);
else
printf("Weight %d; Benefit: 0; Can't reach this exact weight.\n", i);
printf("---\n");
aux = W;
while ((aux > 0) && (last_added[aux] != -1)) {
printf("Added object %d (%d$ %dKg). Space left: %d\n", last_added[aux] + 1, v[last_added[aux]], c[last_added[aux]], aux - c[last_added[aux]]);
aux -= c[last_added[aux]];
}
printf("Total value added: %d$\n", a[W]);
}
int main(int argc, char *argv[]) {
fill_sack();
return 0;
}
This is the classic Greedy algorithm implementation for solving the Fractional Knapsack Problem in C.
Further explanations here

#include <stdio.h>
int n = 5; /* The number of objects */
int c[10] = {12, 1, 2, 1, 4}; /* c[i] is the *COST* of the ith object; i.e. what
YOU PAY to take the object */
int v[10] = {4, 2, 2, 1, 10}; /* v[i] is the *VALUE* of the ith object; i.e.
what YOU GET for taking the object */
int W = 15; /* The maximum weight you can take */
void simple_fill() {
int cur_w;
float tot_v;
int i, maxi;
int used[10];
for (i = 0; i < n; ++i)
used[i] = 0; /* I have not used the ith object yet */
cur_w = W;
while (cur_w > 0) { /* while there's still room*/
/* Find the best object */
maxi = -1;
for (i = 0; i < n; ++i)
if ((used[i] == 0) &&
((maxi == -1) || ((float)v[i]/c[i] > (float)v[maxi]/c[maxi])))
maxi = i;
used[maxi] = 1; /* mark the maxi-th object as used */
cur_w -= c[maxi]; /* with the object in the bag, I can carry less */
tot_v += v[maxi];
if (cur_w >= 0)
printf("Added object %d (%d$, %dKg) completly in the bag. Space left: %d.\n", maxi + 1, v[maxi], c[maxi], cur_w);
else {
printf("Added %d%% (%d$, %dKg) of object %d in the bag.\n", (int)((1 + (float)cur_w/c[maxi]) * 100), v[maxi], c[maxi], maxi + 1);
tot_v -= v[maxi];
tot_v += (1 + (float)cur_w/c[maxi]) * v[maxi];
}
}
printf("Filled the bag with objects worth %.2f$.\n", tot_v);
}
int main(int argc, char *argv[]) {
simple_fill();
return 0;
}
动态规划其实质上是通过开辟记录表,记录已求解过的结果,当再次需要求解的时候,可以直接到
那个记录表中去查找,从而避免重复计算子问题来达到降低时间复杂度的效果。实际上是一个空间
换时间的算法。动态规划,通常可以把指数级的复杂度降低到多项式级别。
一般算法书都会讲能不能用动态规划来求解问题,通常是判断有没有最优解结构,通常是通过“剪
切技术”来判断:即证明问题的一个最优解中,使用的子问题的解本身也必须是最优的。通常是假
设一个子问题不是最优的,那么找到一个最优的子问题来替换这个子问题,那么产生的最优解将优
于已找到的那个最优解,从而矛盾。
其实用不用动态规划来求解问题,还有一个关键是有没有重复的子问题。这也是使用动态规划
与贪心法的区别所在。。贪心法求解的问题也满足最优解结构,只是它能够在每一步都能够“贪婪的
”选出当前唯一的最优子问题,并且当前的选择,是不依赖以前的选择的,通过这种“贪婪的选择”
选到最后时,就得到了全局的最优解了,不会产生重复的子问题。而动态规划,在一步选择的时候,
是通过从以前求出的若干个与本步骤相关的子问题最优解中选择最好的那个,加上这一步的值,来构
造这一步那个子问题的最优解,而如果以前求出的若干个子问题不保存下来,就需要重新求(通常是递
归所致)。动态规划用武之地也无非是保存这些重复的子问题而避免重新求解而达到高效的目的。
动态规划的难点在于写出递推式。动态规划的步骤其实是很固定的,而每一个问题的递推式如何下手
得到会因不同的问题而不同,这是个最关键的问题,没有通用的方法。通常是根据题目的问题,最终要求的问题,都会
有几个数,以两个数M,N为例,然后让求最优值。你就可以使用v[M][N]数组来保存最有解,然后把问题
替换成i,j两个数的问题,试图通过v[i][j]与前面求出来的解建立递推关系。建立递推关系后,你可以简单
的写出递归形式的程序,这个程序只需要加上一条if(v[i][j]已求出) return v[i][j];就轻松改称了动
态规划,这就是lookup的形式。当然如果已经有了递推式,你也很容易写出从底向上推的迭代形式。
一般的算法书讲的动态规划都是来求解最优解的问题,或许最初是用来求解规划问题的,而规划必然是最
优解问题,其实大多数的问题只要存在重复的子问题都可以使用动态规划的思路,就看你的重复的子问题
是不是多的值得使用空间来换时间这个思路了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· [AI/GPT/综述] AI Agent的设计模式综述