2021_GDUT_新生专题训练_动态规划基础

动态规划在我的理解中就是当前状态可以通过已知的状态来推出,也就是常说的最优子结构

背包问题


01背包

简单来讲就是有n件物体,每件物品由价值vi和重量wi,背包容量为m,每件物品最多只能取一次,求最大价值。

用dp[i][j]来表示在考虑前i件物品,容量为j的情况下所能取得的最大价值。 对于第i件物品,有取和不取两种情况,在j<wi的情况下就只能不取了。

1 取

dp[i] [j] = dp[i-1] [j-w[i]] + v[i];也就是说等于取i-1件物品容量为j-w[i]的情况下最大价值加上第i件物品的价值

2 不取

dp[i] [j] = dp[i-1] [j];

最终状态转移方程为dp[i] [j] = max(dp[i-1] [j-w[i]] + v[i] , dp[i] [j] = dp[i-1] [j]);

for(int i=1;i<=n;i++){
    for(int j=w[i];j<=m;j++){
        dp[i][j] = max(dp[i-1][j-w[i]] + v[i], dp[i-1][j]);
    }
}

但我们可以发现每次更新的时候i总是只与i-1有关,所以我们可以压缩掉i这一维的空间,但是

dp[i] [j] = dp[i-1] [j-w[i]] + v[i]的时候j-w[i]是用到了之前的数据,所以在第二层循环的时候需要逆序遍历。

for(int i=1;i<=n;i++){
    for(int j=m;j>=w[i];j--){
        dp[j] = max(dp[j-w[i]] + v[i], dp[j]);
    }
}
送快弟

现在我们有N个配件,他们有不同的价值. 但是我们背包的容量是有限的,因为我们只有一个一级包, 所以我们最多可以装V重量的东西. 但是为了能更好的吃到鸡(不存在的)我们要携带更有价值的配件,请问我们最多能拿多少价值的配件来当快递员呢??

input

输入的第一行是T, 表示有一共要打T场比赛.

每组数据由三行组成.

第一行包含两个整数N和V(N <= 1000, V <= 1000). N表示配件的个数, V表示一级包的大小(系统会更新嘛).

第二行包含N个整数, 表示每一个配件的价值.

第三行包含N个整数, 表示每个配件的重量.

ouput

对每一组数据, 输出我们最多能拿多少价值的配件.

Sample Input
1
10 10
1 3 5 7 9 11 13 15 17 19
19 17 15 13 11 9 7 5 3 1
Sample Output
51

经典01背包,代码如下

#include <iostream>
using namespace std;
#include <algorithm>
#include <string.h>
int n, v;
struct {
    int val;
    int wei;
} node[1005];
int dp[1005][1005];
int main()
{
    int t;
    cin >> t;
    while(t--){
        memset(dp,0,sizeof(dp));
        cin >> n >> v;
        for (int i = 1; i <= n; i++)   cin >> node[i].val;
        for (int i = 1; i <= n; i++)   cin >> node[i].wei;
        
        for (int i = 1; i <= n; i++){
            for (int j = 0; j <= v;j++){
                if(j<node[i].wei)
                    dp[i][j] = dp[i - 1][j];
                else
                    dp[i][j] = max(dp[i - 1][j], dp[i-1][j - node[i].wei] + node[i].val);
            }
        }
        cout << dp[n][v] << endl;
    }
}
CD(背包路径)

You have a long drive by car ahead. You have a tape recorder, but unfortunately your best music is on CDs. You need to have it on tapes so the problem to solve is: you have a tape N minutes long. How to choose tracks from CD to get most out of tape space and have as short unused space as possible.

Assumptions:

  • number of tracks on the CD. does not exceed 20
  • no track is longer than N minutes
  • tracks do not repeat
  • length of each track is expressed as an integer number
  • N is also integer

Program should find the set of tracks which fills the tape best and print it in the same sequence as the tracks are stored on the CD

input

Any number of lines. Each one contains value N, (after space) number of tracks M and durations of the tracks. For example from first line in sample data: N=5, M=3, first track lasts for 1 minute, second one 3 minutes, next one 4 minutes

The input data satisfies the following constraints:

N≤10000
M≤20

ouput

Set of tracks (and durations) which are the correct solutions and string ```sum:`" and sum of duration times.

Sample Input
5 3 1 3 4
10 4 9 8 4 2
20 4 10 5 7 4
90 8 10 23 1 2 3 4 5 7
45 8 4 10 44 43 12 9 8 2
Sample Output
1 4 sum:5
8 2 sum:10
10 5 4 sum:19
10 23 1 2 3 4 5 7 sum:55
4 10 12 9 8 2 sum:45

大致是求在m个数中某些数的和最接近但小于n,并求出其路径

#include <iostream>
using namespace std;
#include <vector>
#include <math.h>
#include <cstdio>
#include <stdio.h>
#include <algorithm>
#include <string.h>
typedef long long ll;
vector <int> ans[10005];
int dp[10005];
int num[10005];
bool flag[25][10005];
int main(){
    int sum,n;
    while(cin >> sum >> n){
        memset(flag,0,sizeof(flag));
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=n;i++){
            cin >> num[i];
        }
        //sort(num+1,num+1+n,less<int>());
        for(int i=n;i>=1;i--){
            for(int j=sum;j>=num[i];j--){
                //dp[j] = max(dp[j],dp[j-num[i]]+num[i]);
                if(dp[j-num[i]]+num[i]>dp[j]){
                    dp[j] = dp[j-num[i]]+num[i];
                    flag[i][j] = 1;
                }
            }
        }
        for(int i = 1, j = dp[sum]; i <= n,j>0; i++){
            if(flag[i][j]) {
                cout << num[i] <<" ";
                j -= num[i];
            }
        }
        cout << "sum:" << dp[sum] << endl;
    }
}

无限背包

与01唯一的不同在于每件物品是可以无限拿取的

代码差不多,只是在二层循环的时候需要正序遍历


多重背包(待补)

有限制条件的背包(待补)

区间dp

区间DP,指的就是对区间的DP,主要的思想是依旧是最优子结构和无后效性的确保, 一般思路就是先对小区间进行操作得到最优解, 然后通过小区间的最优解来得到大区间的最优解。(copy而来)

基本模板

for(int len=2;len<=n;len++){//先枚举长度
    for(int l=1,r=l+len-1;r<=n;l++,r++){//再枚举l
        for(int k=l+1;k<r;k++){//后枚举分界点
            dp[l][r] = min(dp[l][r],dp[l][k]+dp[k+1][r]);
        }
    }
}
//时间复杂度为n^3^
石子合并

传送门

题目描述

在一个圆形操场的四周摆放 N 堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出一个算法,计算出将 N 堆石子合并成 1 堆的最小得分和最大得分。

输入格式

数据的第 11 行是正整数 N,表示有 N 堆石子。

第 22 行有 N个整数,第 i 个整数 ai 表示第 i堆石子的个数。

输出格式

输出共 22 行,第 11 行为最小得分,第 22 行为最大得分。

输入 #1

4
4 5 9 4

输出 #1

43
54

区间dp入门模板题,需要注意的是这里是呈环形,常用的处理方式是把环分成链,这就需要扩展n到2n,使a[i+1]=a[1]…

#include <iostream>
using namespace std;
#include <algorithm>
#include <cstdio>
#include <queue>
#include <vector>
#include <stack>    
#include <string.h>
#include <string>
#include <math.h>
#include <map>
#include <unordered_map>
#include <stdlib.h>
#include <set>
#include <unordered_set>
#include <bitset>
#define IOS std::ios::sync_with_stdio(false);
#define INF 0x3f3f3f3f
typedef long long ll;
typedef unsigned long long ull;
const int N = 1e2+5;
const int mod = 1000000007;
int dp_min[N][N];
int dp_max[N][N];
int pre[N];
int a[N];
int main(){
    memset(dp_min,INF,sizeof(dp_min));
    int n;
    cin >> n;
    for(int i=1;i<=n;i++)
        cin >> a[i],pre[i] = pre[i-1]+a[i]; 
    for(int i=n+1;i<=2*n;i++) a[i] = a[i-n],pre[i] = pre[i-1]+a[i];
    
    for(int i=0;i<=2*n;i++) dp_min[i][i] = 0;
    for(int len=2;len<=n;len++){
        for(int l=1,r=l+len-1;r<=n+n;l++,r++){
            for(int k=l;k<r;k++){
                dp_min[l][r] = min(dp_min[l][r],dp_min[l][k]+dp_min[k+1][r]+(pre[r]-pre[l-1]));
                dp_max[l][r] = max(dp_max[l][r],dp_max[l][k]+dp_max[k+1][r]+(pre[r]-pre[l-1]));
            }
        }
    }

    int ans_min = 1e9,ans_max = 0;
    for(int s=1;s<=n;s++){
        ans_min = min(ans_min,dp_min[s][s+n-1]);
        ans_max = max(ans_max,dp_max[s][s+n-1]);
    }
    cout << ans_min << endl << ans_max;
	return 0;
}
四边形优化

传送门

n^3实在是太大了,在一些数据很小的情况下可以过,但数据较大时是不可以接受的。

第三层循环中寻找分界点的时候是有很多重复的,我们可以用一个s[i] [j]的数组来记录dp[i] [j]的最佳分割点的下标,因为s数组满足四边形不等式所以dp[i] [j]k的范围为s[i] [j-1]到dp[i+1] [j]。综合下来时间复杂度为n^2。

s的初始化令s[i] [i] =i就行了 (我也不太确定)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 内容概要 《计算机试卷1》是一份综合性的计算机基础和应用测试卷,涵盖了计算机硬件、软件、操作系统、网络、多媒体技术等多个领域的知识点。试卷包括单选题和操作应用两大类,单选题部分测试学生对计算机基础知识的掌握,操作应用部分则评估学生对计算机应用软件的实际操作能力。 ### 适用人群 本试卷适用于: - 计算机专业或信息技术相关专业的学生,用于课程学习或考试复习。 - 准备计算机等级考试或职业资格认证的人士,作为实战演练材料。 - 对计算机操作有兴趣的自学者,用于提升个人计算机应用技能。 - 计算机基础教育工作者,作为教学资源或出题参考。 ### 使用场景及目标 1. **学习评估**:作为学校或教育机构对学生计算机基础知识和应用技能的评估工具。 2. **自学测试**:供个人自学者检验自己对计算机知识的掌握程度和操作熟练度。 3. **职业发展**:帮助职场人士通过实际操作练习,提升计算机应用能力,增强工作竞争力。 4. **教学资源**:教师可以用于课堂教学,作为教学内容的补充或学生的课后练习。 5. **竞赛准备**:适合准备计算机相关竞赛的学生,作为强化训练和技能检测的材料。 试卷的目标是通过系统性的题目设计,帮助学生全面复习和巩固计算机基础知识,同时通过实际操作题目,提高学生解决实际问题的能力。通过本试卷的学习与练习,学生将能够更加深入地理解计算机的工作原理,掌握常用软件的使用方法,为未来的学术或职业生涯打下坚实的基础

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值