背包_入门_题解

背包

bone collector

Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like to collect varies of bones , such as dog’s , cow’s , also he went to the grave …
The bone collector had a big bag with a volume of V ,and along his trip of collecting there are a lot of bones , obviously , different bone has different value and different volume, now given the each bone’s value along his trip , can you calculate out the maximum of the total value the bone collector can get ?

input: The first line contain a integer T , the number of cases. Followed by T cases , each case three lines , the first line contain two integer N , V, (N <= 1000 , V <= 1000 )representing the number of bones and the volume of his bag. And the second line contain N integers representing the value of each bone. The third line contain N integers representing the volume of each bone.

**output: **One integer per line representing the maximum of the total value (this number will be less than 2[sup]31[/sup]).

分析

​ 这是一个01背包问题,其所要达到的目标是将搜集的骨头的价值最大化,但是限制条件是骨头有一定的体积,而最大体积是有限制的。

#include <iostream>
#include <math.h>
#include <algorithm>
using namespace std;

int main()
{
    int T, N, V;
    int value[1001]/*代表着骨头的价值*/, volume[1001]/*代表着骨头的体积*/;
    int bag[1001];
    cin >> T;
    while(T--)
    {
        cin >> N >> V;
        //读入数据
        for(int i = 0; i < N; i++) cin >> value[i];
        for(int i = 0; i < N; i++) cin >> volume[i];
        //对背包进行初始化
        for(int i = 0; i <= V; i++)
        {
            bag[i] = 0;
        }
        
        for(int i = 0; i < N; i++)//选择第i个骨头要不要拿,i的范围也自然从第0个到第N-1个
        {
            //因为是01背包,所以从限制的体积开始向前走,走到当前选中的骨头的体积为止
            for(int j = V; j >= volume[i]; j--)
            {
                //背包的状态转移方程
                bag[j] = max(bag[j], bag[j-volume[i]] + value[i]);
            }
        }
        cout << bag[V] << endl;
    }
    return 0;
}

Piggy-Bank

Problem Description

Before ACM can do anything, a budget must be prepared and the necessary financial support obtained. The main income for this action comes from Irreversibly Bound Money (IBM). The idea behind is simple. Whenever some ACM member has any small money, he takes all the coins and throws them into a piggy-bank. You know that this process is irreversible, the coins cannot be removed without breaking the pig. After a sufficiently long time, there should be enough cash in the piggy-bank to pay everything that needs to be paid.

But there is a big problem with piggy-banks. It is not possible to determine how much money is inside. So we might break the pig into pieces only to find out that there is not enough money. Clearly, we want to avoid this unpleasant situation. The only possibility is to weigh the piggy-bank and try to guess how many coins are inside. Assume that we are able to determine the weight of the pig exactly and that we know the weights of all coins of a given currency. Then there is some minimum amount of money in the piggy-bank that we can guarantee. Your task is to find out this worst case and determine the minimum amount of cash inside the piggy-bank. We need your help. No more prematurely broken pigs!

Input

The input consists of T test cases. The number of them (T) is given on the first line of the input file. Each test case begins with a line containing two integers E and F. They indicate the weight of an empty pig and of the pig filled with coins. Both weights are given in grams. No pig will weigh more than 10 kg, that means 1 <= E <= F <= 10000. On the second line of each test case, there is an integer number N (1 <= N <= 500) that gives the number of various coins used in the given currency. Following this are exactly N lines, each specifying one coin type. These lines contain two integers each, Pand W (1 <= P <= 50000, 1 <= W <=10000). P is the value of the coin in monetary units, W is it’s weight in grams.

Output

Print exactly one line of output for each test case. The line must contain the sentence “The minimum amount of money in the piggy-bank is X.” where X is the minimum amount of money that can be achieved using coins with the given total weight. If the weight cannot be reached exactly, print a line “This is impossible.”.

分析

  • 题目中给出的限制条件是存钱罐所含的钱的质量,而钱本身是有一定的质量,目标是达到最小的价值;

  • 每一种钱都可能不止一种,理论上可以有无限个,所以是完全背包问题;

  • 隐含条件是最后的所计算出的前都必须与存钱罐中一致,所以初始化的时候要当心

#include <iostream>
using namespace std;

int main()
{
    int T;
    cin >> T;
    while(T--)
    {
        int w1, w2, w;//w代表着钱的总重
        int coin_kind_num;//钱币的种类数
        int value[10001], weight[10001];
        int bag[10001];
        cin >> w1 >> w2;
        w = w2 - w1;
        cin >> coin_kind_num;

        for(int i = 0; i < coin_kind_num; i++) cin >> value[i] >> weight[i];
        //由于题中要求是要将小猪的重量与选中的钱币总重相同,所以初始化为无穷大
        for(int i = 0; i <= w; i++) bag[i] = 0x3f3f3f;
        bag[0] = 0;//但是第0个需要初始化为0
        //选第i种钱币
        for(int i = 0; i < coin_kind_num; i++)
        {
            //完全背包问题,所以需要从当前的限制值走到最大限制值
            for(int j = weight[i]; j <= w; j++)
            {
                //由于是算最小价值,所以需要用min,而非max
                bag[j] = min(bag[j], bag[j - weight[i]] + value[i]);
            }
        }
        if(bag[w] == 0x3f3f3f)
        {
            cout << "This is impossible." << endl;
        }
        else
            printf("The minimum amount of money in the piggy-bank is %d.\n", bag[w]);
    }
    return 0;
}

悼念512汶川大地震遇难同胞——珍惜现在,感恩生活

Problem Description

为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买。请问:你用有限的资金最多能采购多少公斤粮食呢?

Input

输入数据首先包含一个正整数C,表示有C组测试用例,每组测试用例的第一行是两个整数n和m(1<=n<=100, 1<=m<=100),分别表示经费的金额和大米的种类,然后是m行数据,每行包含3个数p,h和c(1<=p<=20,1<=h<=200,1<=c<=20),分别表示每袋的价格、每袋的重量以及对应种类大米的袋数

Output

对于每组测试数据,请输出能够购买大米的最多重量,你可以假设经费买不光所有的大米,并且经费你可以不用完。每个实例的输出占一行。

分析

  • 题中给出了每种米的袋数,说明这是01背包和完全背包的结合体。可以用二进制的思想来将这个问题转换为01背包问题,代码如下:
int count;
void read_in()
{
    int p, w, num;
    cin >> p >> w >> num;//读入价格,重量,每种米的袋数
    int k = 2, i;
    //将选中一袋米的情况记录下来
    price[count] = p;
    weight[count] = w;
    count++;//每次记录count必须自加1

    //i代表着当前还剩多少袋,由于之前已经读入1袋,所以从i-1开始
    for(i = num-1; i-k >= 0/*表示这次选中了k袋之后,这次能不能够满足拿出k袋的需求*/; )
    {
        //读入相应的选中的k袋对应的价格和重量
        price[count] = k * p;
        weight[count] = k * w;
        count++;
        //i变为拿走k袋后剩余的袋数
        i = i - k;
        //k翻倍
        k = 2 * k;
    }
    //如果最后剩余的袋数不为0,且不能满足二进制的要求
    if(i != 0)
    {
        price[count] = i * p;
        weight[count] = i * w;
        count++;
    }
}
  • 限制条件是资金,但是资金可以不用花光,所以初始化时仅需全设置为0;目标是大米的重量最大

    #include <iostream>
    using namespace std;
    int price[1001], weight[1001];
    int count;
    
    void read_in()
    {
        int p, w, num;
        cin >> p >> w >> num;//读入价格,重量,每种米的袋数
        int k = 2, i;
        //将选中一袋米的情况记录下来
        price[count] = p;
        weight[count] = w;
        count++;//每次记录count必须自加1
    
        //i代表着当前还剩多少袋,由于之前已经读入1袋,所以从i-1开始
        for(i = num-1; i-k >= 0/*表示这次选中了k袋之后,这次能不能够满足拿出k袋的需求*/; )
        {
            //读入相应的选中的k袋对应的价格和重量
            price[count] = k * p;
            weight[count] = k * w;
            count++;
            //i变为拿走k袋后剩余的袋数
            i = i - k;
            //k翻倍
            k = 2 * k;
        }
        //如果最后剩余的袋数不为0,且不能满足二进制的要求
        if(i != 0)
        {
            price[count] = i * p;
            weight[count] = i * w;
            count++;
        }
    }
    
    int main()
    {
        int T;
        cin >> T;
        while(T--)
        {
            int money, kinds;
            cin >> money >> kinds;
            count = 0;//初始化count
            for(int i = 0; i < kinds; i++)//读入k种米
            {
                read_in();
            }
            int bag[101];
            //因为已经转化为01背包,初始化为0
            for(int i = 0; i <= money; i++) bag[i] = 0;
            for(int i = 0; i < count/*这时选的物品数并不是kinds,而是转化后的count!!!!*/; i++)
            {
                for(int j = money; j >= price[i]; j--)//01背包,从后往前
                {
                    bag[j] = max(bag[j], bag[j - price[i]] + weight[i]);
                }
            }
            cout << bag[money] << endl;
        }
        return 0;
    }
    

FATE

Problem Description
最近 xhd 正在玩一款叫做FATE的游戏,为了得到极品装备,xhd 在不停的杀怪做任务。久而久之 xhd 开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd 升掉最后一级还需n的经验值,xhd 还留有m的忍耐度,每杀一个怪 xhd 会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd 就不会玩这游戏。xhd 还说了他最多只杀s只怪。请问他能升掉这最后一级吗?

Input

输入数据有多组,对于每组数据第一行输入n,m,k,s(0 < n,m,k,s < 100)四个正整数。分别表示还需的经验值,保留的忍耐度,怪的种数和最多的杀怪数。接下来输入k行数据。每行数据输入两个正整数a,b(0 < a,b < 20);分别表示杀掉一只这种怪xhd会得到的经验值和会减掉的忍耐度。(每种怪都有无数个)

Output

输出升完这级还能保留的最大忍耐度,如果无法升完这级输出-1。

分析

  • 二维背包,限制条件分别为忍耐度,最多杀怪数,目标是打怪时花费的最小忍耐度,并不是打怪获得的最大经验值,但也要判断会不会达到要求的经验值

    #include <iostream>
    using namespace std;
    int v[101], w[101];
    int bag[101][101];
    
    int main()
    {
        int jin, ren, kinds, num_max;
        while(cin >> jin >> ren >> kinds >> num_max)
        {
            for(int i = 0; i < kinds; i++) cin >> v[i] >> w[i];
            for(int i = 0; i <= ren; i++)
            {
                for(int j = 0; j <= num_max; j++)
                {
                    bag[i][j] = 0;//二维背包,两个限制值
                }
            }
            int mini = 101;//最小花费忍耐度
            for(int i = 0; i < kinds; i++)
            {
                //两个for循环,外层控制忍耐度,内层控制杀怪数
                for(int j = w[i]; j <= ren; j++)
                {
                    for(int k = 1; k <= num_max; k++)
                    {
                        //状态转移
                        bag[j][k] = max(bag[j][k], bag[j-w[i]][k-1] + v[i]);
                        //更新最小花费忍耐度
                        if(bag[j][k] >= jin)
                        {
                            mini = min(mini, j);
                        }
                    }
                }
            }
            //要不要输出最小忍耐度的条件
            if(bag[ren][num_max] >= jin)
            {
                cout << ren - mini << endl;
            }
            else
            {
                cout << -1 << endl;
            }
        }
        return 0;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: simple_ssti_1 是一个使用 Flask 框架编写的 web CTF 题目,主要涉及到服务器端模板注入 (Server-Side Template Injection, SSTI) 的问题。 这个题目中,使用者可以在输入框中输入任意字符串,然后后端会将这个字符串嵌入到一个 Flask 模板中,并将渲染后的页面返回给用户。攻击者可以在输入框中输入包含 SSTI 代码的字符串,从而在服务器上执行任意代码。 为了解决这个问题,可以使用 Flask 的 Jinja2 模板引擎提供的 safe 过滤器来过滤用户输入,防止用户输入的字符串被作为代码执行。或者在编写模板时,尽量避免使用可执行的表达式,而是将变量放在 HTML 标签中输出。 这个题目是一个很好的 SSTI 注入练习题目,可以帮助我们了解 SSTI 注入的危害和防御方法。 ### 回答2: Simple_ssti_1是BugsKiller CTF比赛中的一道Web题目,考察的知识点是SSTI(Server-Side Template Injection)模板注入,需要寻找漏洞点并利用SSTI漏洞构造payload,达到读取/执行任意代码的目的。 首先,我们下载题目附件simple_ssti_1.zip,获得题目源代码及相关文件。查看代码,发现以下几点: 1. 程序的入口是index.php文件,包含了一个GET参数tpl,可控注入的点在这里。 2. 向模板文件simple_ssti_1_template.html中传入tpl参数,在该文件中执行了{{tpl}}操作,将tpl参数进行了模板渲染。 3. SSTI的注入点在于,如果我们的攻击payload中包含了一些特殊模板语法的操作符,如{{3*3}}、{{config}}等,这些操作符会被解析器自动执行,导致代码注入进去。 从上述代码的分析可知,我们首先需要构造包含有SSTI操作符的payload才能进行下一步的SSTI构造。继续观察代码,我们发现一个{{config}}变量被渲染在了simple_ssti_1_template.html的头部中,我们可以通过注入payload来构造一个同名的config变量,从而读取根目录的敏感文件/flag.php。 构造payload : {{config.__class__.__init__.__globals__['os'].popen('cat flag.php').read()}} 这个SSTI注入的payload实现了直接运行命令cat flag.php然后读取文件的操作。注入的{{config}}变量实际上是一个自定义的config字典,含有很多内置函数,比如__class__.__init__.__globals__,它的作用是获取全局变量__builtins__,然后通过这个全局字典来获取os模块,之后就可以使用os模块的popen函数运行cat命令来获取flag.php文件内容了。 最终的payload为: ?tpl={{config.__class__.__init__.__globals__['os'].popen('cat /flag.php').read()}} 再通过浏览器发送带有payload的GET请求,就可以读取/root/flag.php中的flag了。 ### 回答3: 简单SSTI 1是一道基于SSTI漏洞的Web安全挑战题目,该题的难度属于初级。本题需要掌握一定的SSTI漏洞的相关知识和技巧,以及对模板中的变量注入点的识别和利用能力。 首先,我们尝试在输入框中输入简单的Python表达式,例如{{2+2}},并提交请求,发现得到的响应结果为4,表明该网站存在SSTI漏洞。接着,我们可以构造一些特殊的表达式进行测试,例如{{123456789}}, {{2**100}}, {{'hello ' + 'world'}}, 发现均能得到正确的响应结果。 接着我们需要进行变量注入点的识别和利用,这里,我们可以通过利用flask框架中的特殊变量,例如request、g等来实现变量注入,例如{{config}},可以获得flask的配置信息,{{request}}可以获得当前请求的一些信息。需要注意的是,在实战中,这些利用方式可能会受到服务器的限制,无法完全实现。 最后,我们需要尝试获取敏感信息或者升级我们的权限,例如{{''.__class__.mro()[1].__subclasses__()[71]('/etc/passwd').read()}},可以获取到服务器上/etc/passwd文件的内容。 总之,简单的SSTI漏洞需要熟练掌握SSTI漏洞的相关知识和技巧,识别变量注入点并利用它们获取敏感信息和升级权限,可以通过CTF题目学习,提高自己的Web安全攻防能力。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值