蓝桥杯练习-3.5

蓝桥杯练习-3.5

视频学习

2019年蓝桥杯训练营(C++) 13.1背包问题视频讲解

完全背包问题

当前有N种物品,第i种物品的体积是weight[i],价值是value[i]。每种物品的数量都是无限的,可以任意选择若干件。

现有容量为V的背包。请你放入若干物品,使总体积不超过V,并且总价值尽可能大。

可以用多重背包的思想来解决完全背包:

for (int i = 1; i <= N; i++) {
	for (int j = 0; j <= V; j++) {
		for (int k = 0; k * weight[i] <= j; k++) {
		dp[i][j] = max(dp[i - 1][j - weight[i] * k] + k * value[i], dp[i][j]);
		}
	}
}
时间优化
for (int i = 1; i <= n; i++) {
    for (int j = 0; j <= v; j++) {
        if (j >=  weight[i]) {
            dp[i][j] = max(dp[i][j - weight[i]] + value[i], dp[i-1][j]);
            else {
                dp[i][j] = dp[i - 1][j];
            }
        }
    }
}
空间优化
for (int i = 1; i <= n; i++) {
	for (int j = weight[i]; j <= V; j++) {
	dp[j] = max(dp[j - weight[i]] + value[i], d[j]);
	}
}

注意到,时间优化后,我们是用dp[i] [j - weight[i]]的值去更新dp[i] [j]。除了第一次会跟dp[i - 1] [j]比较,如果前者较大,后面则跟dp[i - 1] [j]无关了,一直是dp[i] [j - weight[i]]的值去更新dp[i] [j],因为每一次体积够了,都会加一次value[i]。

代码和01背包的空间优化代码仅仅是内存的for循环变了方向。(对于第 i 次主循环中,可拿的物品永远是第 i 件物品,随着背包容量的增大,会进行多次判断是否拿取第 i 件物品)

之前不能用正向是因为,我们不能用更新后的值,而这里恰好要使用更新后的值,因为在i的大循环中,随着容量的增加(正向更新),可用的容量越来越大,可以多次判断是否拿第i件物品更加划算。

代码实现(未空间优化):
#include <iostream>
#include <string>
using namespace std;
int dp[21][1010];
int w[21], v[21];//w表示物品的体积,v表示物品的价值
int main()
{
    int N, V;
    cin >> N >> V;
    for (int i = 1; i <= N; i++)
    {
        cin >> v[i] >> w[i];
    }
    for (int i = 1; i <= N; i++)
    {
        for (int j = 0; j <= V; j++)
        {
            if (j >= w[i])
            {
                dp[i][j] = max(dp[i - 1][j], dp[i][j - w[i]] + v[i]);
            }
            else
                dp[i][j] = dp[i - 1][j];
        }
    }
    cout << dp[N][V] << endl;
    return 0;
}
代码(空间优化):
#include <iostream>
#include <cstring>
using namespace std;
int dp[1010];
int w[21], v[21];//w[i]表示物品的体积,v[i]表示单个物品i的价值
int main()
{
    int N, V;
    cin >> N >> V;
    for (int i = 1; i <= N; i++)
    {
        cin >> v[i] >> w[i];
    }
    for (int i = 1; i <= N; i++)
    {
        for (int j = w[i]; j <= V; j++)//枚举j要正序
        {
            dp[j] = max(dp[j], dp[j - w[i]] + v[i]);
        }
    }
    cout << dp[V] <<endl;
    return 0;
}

多重背包问题-二进制优化

用1,2,…2^(k - 1) 可以组成0到2^k - 1上的任意整数,首先要找到2^k - 1<ni的最大k值,也就是 ni - 2^k + 1 > 0。如果再加上ni - 2^k + 1,那么又可以组成ni - 2^k + 1到 ni上的任意整数。所以把多重背包每个物品的数量分成二进制一组,把每种物品的数量分组变成二进制,这样i循环就可以循环这些组数,这样可以不用去枚举k,变为01背包问题。

❕左移在不考虑溢出的情况下就相当于成乘2

代码实现(空间优化过且二进制优化):
#include <iostream>
#include <algorithm>
using namespace std;
int n[110], w[110], v[110];//n[i]表示第i件物品有多少个
int nw[110], nv[110];//nw表示分完组的每一组的物品总体积,nv表示分完组的每一组物品的总价值
int dp[5010];
int main()
{
    int N, V;
    cin >> N >> V;
    for (int i = 1; i <= N; i++)
    {
        cin >> v[i] >> w[i] >> n[i];
    }
    int cnt = 0;
    for (int i = 1; i <= N; i++)//进行二进制拆分
    {
        int k;//找到最大的满足条件的k值
        for (int k = 1; n[i] - (1 << k) + 1 > 0; ++k)
        {
            nw[cnt] = (1 << (k - 1)) * w[i];
            nv[cnt] = (1 << (k - 1)) * v[i];
            ++cnt;
        }
        --k;//处理最后一组,也就是ni - 2^k + 1那组,因为for循环结束了,k也会加一,所以要减去
        nw[cnt] = (n[i] - (1 << k) + 1) * w[i];
        nv[cnt] = (n[i] - (1 << k) + 1) * v[i];
        ++cnt;
    }
    for (int i = 0; i < cnt; ++i)//最后用01背包即可
    {
        for (int j = V; j >= nw[i]; --j)
        {
            dp[j] = max(dp[j], dp[j - nw[i]] + nv[i]);
        }
    }
    cout << dp[V] << endl;
    return 0;
}

代码学习

试题 基础练习 矩阵乘法

问题描述

给定一个N阶矩阵A,输出A的M次幂(M是非负整数)
  例如:
  A =
  1 2
  3 4
  A的2次幂
  7 10
  15 22

输入格式

第一行是一个正整数N、M(1<=N<=30, 0<=M<=5),表示矩阵A的阶数和要求的幂数
  接下来N行,每行N个绝对值不超过10的非负整数,描述矩阵A的值

输出格式

输出共N行,每行N个整数,表示A的M次幂所对应的矩阵。相邻的数之间用一个空格隔开

样例输入

2 2
1 2
3 4

样例输出

7 10
15 22

思路:首先得明白矩阵乘法的定义,这里因为是多次幂,所以可以设置俩个二维数组,事先让俩个二维数组一模一样,然后在乘的时候,让b数组乘a数组(其实应该是a乘a),这样处理的好处在于,矩阵乘法只能一个一个乘,如果幂次超过2次,我们可以把第一次的积的结果赋值给b数组,而a数组还是原来没乘的,最开始输入的,下一次b数组乘a数组便是三次幂的乘积。同时需要第三个数组先把积存起来,再赋值给b数组。

代码实现:
#include <cstdio>
#include <iostream>
using namespace std;
int n, m;
int a[31][31];
int b[31][31];
int c[31][31];
void f(void)//注意这种无类型,无参数的函数的使用,有时候很有用
{
    if (m == 0)
    {
        for(int i=1; i<=n; ++i)
        {
            for(int j=1; j<=n; ++j)
  		 {
    			if(i==j)
     				a[i][j] = 1;
   			else
     				a[i][j] = 0;
  		 }
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                printf("%d ", a[i][j]);
            }
   			printf("\n");
        }
        return;
    }
    if (m == 1)
    {
        for(int i=1; i<=n; ++i)
        {
            for(int j=1; j<=n; ++j)
                printf("%d ", a[i][j]);
            printf("\n");
        }
 	 return;
    }
    for (int t = 1; t < m; ++t)
    {
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                int sum = 0;
                for (int k = 1; k <= n; ++k)
                {
                    sum += b[i][k] * a[k][j];
                    c[i][j] = sum;
                }
            }
        }
        for (int i = 1; i <= n; ++i)
        {
            for (int j = 1; j <= n; ++j)
            {
                b[i][j] = c[i][j];
            }
        }
    }
    for(int i=1; i<=n; ++i)
    {
        for(int j=1; j<=n; ++j)
            printf("%d ", c[i][j]);
        printf("\n");
    }
}
int main()
{
    cin >> n >> m;
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            cin >> a[i][j];
        }
    }
    for (int i = 1; i <= n; ++i)
    {
        for (int j = 1; j <= n; ++j)
        {
            b[i][j] = a[i][j];
        }
    }
    f();
    return 0;

注意这种无类型,无参数的函数的使用,有时候很有用

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 1024 设计师:白松林 返回首页