洛谷题单-动态规划的引入

动态规划的引入

  • P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles 题解
    • 解法一: 从上往下推用dp
  • P1048 [NOIP2005 普及组] 采药 题解
    • 解法一: 一维01背包
  • P2196 [NOIP1996 提高组] 挖地雷 题解
    • 解法一: dfs暴搜
    • 解法二: dp
    • 解法三: 树形dp
  • P1434 [SHOI2002] 滑雪
    • 解法一: 记忆化搜索
  • P4017 最大食物链计数
    • 解法一: 记忆化搜索
    • 解法二:拓扑排序
  • P1115 最大子段和 题解
    • 解法一: dp
  • P1802 5 倍经验日
    • 解法一: dp 01背包
  • [NOIP2002 普及组] 过河卒 题解

P1216 [USACO1.5] [IOI1994]数字三角形 Number Triangles 题解

解法一: 从上往下推用dp

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<string>
using namespace std;
#define rint register int
inline void read(int &x)//读入优化 
{
    x=0;int w=1;char ch=getchar();
    while(!isdigit(ch)&&ch!='-')ch=getchar();
    if(ch=='-')w=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^'0'),ch=getchar();
    x=x*w;
}
const int maxn=1000+10;
int n,a[maxn][maxn],ans; 
int main()
{
    read(n);
    for(rint i=1;i<=n;i++)
    {
        for(rint j=1;j<=i;j++)
        {
            read(a[i][j]);
            a[i][j]+=max(a[i-1][j-1],a[i-1][j]);//本题最重要的步骤 
            //a[i][j]代表从上往下到达i行j列这个点所能达到的和的最大值 
            ans=max(ans,a[i][j]);//在所有答案中找最大的(与在最底下一层找最大值是一样的)
        }
    }//边读入边计算,随手优化一下常数(其实只是懒得打for循环) 
    cout<<ans<<endl;
    return 0; 
} 

P1048 [NOIP2005 普及组] 采药 题解

记忆化搜索和动态规划如何联系起来
dp和记忆化搜索都是可以省略中间重复计算的值, 每个值只计算一遍

解法一: 一维01背包

#include "stdio.h"
#include "iostream"
using namespace std;
int w[105], val[105];
int dp[1005];
int main()
{
    int t,m,res=-1;    
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&w[i],&val[i]);
    }
    for(int i=1;i<=m;i++) 
    {
        for(int j=t;j>=0;j--) 
        {
            if(j>=w[i])
            {
                dp[j]=max(dp[j-w[i]]+val[i], dp[j]);
            }
        }
    }    
    printf("%d",dp[t]);
    return 0;
}

P2196 [NOIP1996 提高组] 挖地雷 题解

解法一: dfs暴搜

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int N = 22;
int a[N], g[N][N], b[N], ans[N], path[N];
int n, maxx, cnt;

//判断是否搜到头了
bool check(int cur)//false 代表还能继续往下挖, true代表到头了
{
    for(int i = 1; i <= n; ++ i)
        if (g[cur][i] && !b[i]) return false;
    return true;
}
void dfs(int cur, int step, int sum)
{
    if(check(cur))
    {
        if(sum > maxx)
        {
            maxx = sum;
            cnt = step;
            for (int i = 1; i <= step; ++ i) ans[i] = path[i];
        }
        return ;
    }
    
    for (int i = 1; i <= n; ++ i)
    {
        if (b[i] == 0 && g[cur][i])
        {
            b[i] = 1;
            path[step + 1] = i;//也可以用递归的方法去记录path 比如path[i] = j; 记录下答案的终点就行, 然后从终点递归往前遍历
            dfs(i, step + 1, sum + a[i]);
            b[i] = 0;
        }
    }
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++ i) cin >> a[i];
    
    for (int i = 1; i < n; ++ i)
    {
        for (int j = i + 1; j <= n; ++ j)
         cin >> g[i][j];
    }
    for(int i = 1; i <= n; ++ i)
    {
        b[i] = 1;
        path[1] = i;
        dfs(i, 1, a[i]);
        b[i] = 0;
    }
    
    for (int i = 1; i <= cnt; ++ i) cout << ans[i] << " ";
    cout << endl;
    cout << maxx;
    return 0;
}

解法二: dp

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
const int N = 22;
int g[N][N], f[N], ans[N], maxx, n, a[N], endpos;

void dfs(int x)
{
    if (ans[x] != 0) dfs(ans[x]);//递归
    cout << x << " ";
}
int main()
{
    cin >> n;
    for (int i = 1; i <= n; ++ i) cin >> a[i];
    for (int i = 1; i < n; ++ i)
        for (int j = i + 1; j <= n; ++ j)
            cin >> g[i][j];
            
    f[1] = a[1];
    for (int i = 2; i <= n; ++ i)
    {
        f[i] = a[i]; 
        for (int j = i - 1; j > 0; --j)
            if (g[j][i] && f[i] < f[j] + a[i])
            {//站在i这个点遍历其他点j能否到i并且更新i, 因此是g[j][i]而不是g[i][j];
                f[i] = f[j] + a[i];
                ans[i] = j;// i 是从 j转移过来的
            }
        if (f[i] > maxx) 
        {
            maxx = f[i]; 
            endpos = i;
        }
    }
    
    dfs(endpos);
    cout << endl;
    cout << maxx;
    return 0;
}

解法三: 树形dp

这题展开是一个图, 图的话 就可以联想到树形dp, 后面学树形dp的时候再去看看这题;
不知道为啥我这段代码会wa两个点

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 22, M = 502;
int h[N], ne[N], val[N], e[M], idx = 0;
int n, maxx, ans[N], startpos, dp[N], pre[N];
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
//dp[i] 表示i节点的最大值
int dfs(int u, int fa)
{
    if (dp[u]) return dp[u];// 记忆化搜索
    for (int i = h[u]; ~i; i = ne[i])
    {
        int v = e[i];
        if (v == fa) continue;
        dp[v] = dfs(v, u);//收集v节点的值
        if(dp[v] > dp[u]) //当前dp[u]还没有加上w[u], 直接比较就行,
        {
            dp[u] = dp[v];
            pre[u] = v;
        }
    }
    return dp[u] + val[u];
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for (int i = 1; i <= n; ++ i) cin >> val[i];
    
    for (int i = 1; i < n; ++ i)
        for (int j = i + 1; j <= n; ++ j)
        {
            int x;
            cin >> x;
            if (x) add(i, j);
        }
    
    for (int i = 1; i <= n; ++ i)
    {
        memset(dp, 0, sizeof(dp));
        int tmp = dfs(i, 0);
        if (tmp > maxx)
        {
            maxx = tmp;
            startpos = i;
        }
    }
    
    while (startpos)
    {
        cout << startpos << " ";
        startpos = pre[startpos];
    }
    
    cout << endl << maxx;
    
    return 0;
}

P1434 [SHOI2002] 滑雪

解法一: 记忆化搜索

看了一些dp的做法(也没完全看出来, 感觉dp的写法也长得很想记忆化搜索), 觉得这道题还是一个搜索的做法, 但是记忆化搜索和dp思想很类似, 少部分dp和记忆化搜索的代码可以相互转化, 可能这样就可以和dp联系起来了把;详见下面这篇博客
记忆化搜索和动态规划如何联系起来

#include<iostream>
#include<string>
using namespace std;
const int N = 110;
int m, n;
int f[N][N];
int dx[4] = {0, 1, 0, -1}, dy[4] = {-1, 0, 1, 0};
int h[N][N];
int ans;

int dfs(int x, int y)
{
    if (f[x][y]) return f[x][y];//记忆化搜索
    f[x][y] = 1;
    for (int i = 0; i < 4; ++ i)
    {
        int xx = x + dx[i], yy = y + dy[i];
        if (xx >= 0 && xx < n && yy >= 0 && yy < m && h[xx][yy] < h[x][y]) 
            f[x][y] = max(f[x][y], dfs(xx, yy) + 1);//记忆化搜索
    }
    
    return f[x][y];
}
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j < m; ++ j) cin >> h[i][j];
    
    for (int i = 0; i < n; ++ i)
        for (int j = 0; j < m; ++ j)
            ans = max(ans, dfs(i, j));
    cout << ans;
}

P4017 最大食物链计数

解法一: 记忆化搜索

读取数据的时候预先处理好哪些是食物链顶端的动物, 然后dfs这些动物就行了

#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N = 5e5 + 10;
int n, m;
vector<int> eat[N];
int f[N], isNotTop[N];
int mod = 80112002, ans;
int dfs(int u)
{
    int res = 0;
    if (eat[u].size() == 0) return f[u] = 1;//记忆化搜索
    for (int i = 0; i < eat[u].size(); ++i)
    {
        int v = eat[u][i];
        if (!f[v]) res = (res + dfs(v)) % mod;
        else res = (res + f[v]) % mod;
    }
    return f[u] = res; //记忆化搜索
}
int main()
{
    cin >> n >> m;
    for(int i = 0; i < m; ++i)
    {
        
        int a, b;
        cin >> a >>b;
        eat[b].push_back(a);
        isNotTop[a] = 1;
    }
    for (int i = 1; i <= n; ++ i)
    {
        if (isNotTop[i] == 0) ans = (ans + dfs(i)) % mod;
    }
    cout << ans;
    return 0;
}

解法二:拓扑排序

拓扑排序: 把入度为0的节点删掉并且删掉其相关的边, 重复此步骤直到途中所有节点都删掉了, 节点删掉的顺序就是拓扑排序;
我觉得拓扑排序相关的算法就是在 找到下一个入度为0的节点的时候执行相关操作
无后效性:无后向性是指 以前出现状态 和 以前状态的变化过程 不会影响 将来的变化。
知乎上的问题
在这里插入图片描述

#include<iostream>
#include<cstring>
#include <queue>
using namespace std;
const int N = 6e3, M = 5e6 + 2;
int n, m, mod = 80112002;
int out[N], in[N];// in[b]表示b可以吃的物种数量, out[a]表示可以吃a的物种数量
int h[N], e[M], ne[M], idx;
int f[N], ans;
queue<int> q; 
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void topo()
{
    for (int i = 1; i <= n; ++ i)
    {
        if (in[i] == 0) 
        {
            q.push(i);
            f[i] ++;
        }
    }
    
    while (!q.empty())
    {
        int u = q.front();
        q.pop();
        for (int i = h[u]; ~i; i = ne[i])
        {
            int v = e[i];
            f[v] = (f[v] + f[u]) % mod;
            in[v]--;
            if (in[v] == 0) 
            {
                q.push(v);
            }
        }
    }
}
int main()
{
    memset(h, -1, sizeof h);//这里很重要, 之前我不想memset , for循环中循环中断条件是i!=0, 但其实一开始 ne[idx] = h[a], h[a]的初始值就是0,导致循环只执行一次就没了
    cin >> n >> m;
    for (int i = 0; i < m; ++ i)
    {
        int a, b;
        cin >> a >> b;
        add(a, b);
        out[a] ++ ;
        in[b] ++;
    }
    topo();
    for (int i = 1; i <= n; ++ i) 
        if (out[i] == 0)
            ans = (ans + f[i]) % mod;
    cout << ans;
}

P1115 最大子段和 题解

解法一: dp

第一个数为一个有效序列
如果一个数加上上一个有效序列得到的结果比这个数大,那么该数也属于这个有效序列。
如果一个数加上上一个有效序列得到的结果比这个数小,那么这个数单独成为一个新的有效序列
在执行上述处理的过程中实时更新当前有效序列的所有元素之和并取最大值。

#include<iostream>
#include<cstring>
#include <queue>
using namespace std;
int ans = 1e-9, a, b, n;
int main()
{
    cin >> n;
    for (int i = 0; i < n; ++ i)
    {
        cin >> a;//a: 新的数; b: 上一个有效序列; ans: 找到最大的有效序列(即为答案)
        if (i == 0) b = a;
        else b = max(b + a, a);
        ans = max(ans, b);
    }
    cout << ans;
    return 0;
}

P1802 5 倍经验日

本题有两个决策:

  • 上边那个循环是指剩余药水j>=use[i]时的决策,
  • 而下边这个循环是指j<use[i]时的决策,此时j要满足j<use[i],也就是j<=use[i]-1,所以用use[i]-1来给j赋值
    有一个问题: 会不会一个重量j 对一个物体i既选择了win也选择了lose?
    答案: 不会, 因为我们分类的时候 对一个j要么>use[i]要么<use[i], 大于use[i]的时候, 要么选择win[i], 要么选择lose[i], 但是小于use[i]的时候, 只能选择lose[i]

解法一: dp 01背包

#include<iostream>
#include<cstring>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 1e3 + 10;
int n, m;
int lose[N], win[N], use[N], f[N];
int main()
{
    cin >> n >> m;
    for (int i = 0; i < n; ++ i)
    {
        cin >> lose[i] >> win[i] >> use[i];
    }
    
    for (int i = 0; i < n; ++ i)
    {
        for (int j = m; j >= use[i]; --j)//01背包优化成一维的都是 从后往前推, 为了防止一个物品被重复选择
        {
            f[j] = max(f[j] + lose[i], f[j - use[i]] + win[i]);//01背包状态转移方程, 选或不选
        }
        for (int j = use[i] - 1; j >= 0; -- j) 
        	f[j] += lose[i];
    }
    printf("%lld",5ll*f[m]);
    return 0;
}

[NOIP2002 普及组] 过河卒 题解

这题看题解就行了, 写的很详细
后面两种优化方法在学01背包的时候好像用到过

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/416317.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c++学习记录 vector容器—插入和删除

函数原型&#xff1a; push_back(ele); //尾部插入元素elepop_back(); //删除最后一个元素insert(const_iterator pos,ele); …

2024年腾讯云值得买的云服务器配置推荐,前十名!

腾讯云服务器多少钱一年&#xff1f;62元一年起&#xff0c;2核2G3M配置&#xff0c;腾讯云2核4G5M轻量应用服务器218元一年、756元3年&#xff0c;4核16G12M服务器32元1个月、312元一年&#xff0c;8核32G22M服务器115元1个月、345元3个月&#xff0c;腾讯云服务器网txyfwq.co…

华为数通方向HCIP-DataCom H12-821题库(单选题:501-520)

第501题 三台交换机运行RSTP协议,拓扑和配置情况如图所示。那么以下关于根桥的描述,正确的是哪一项? A、根桥是SWA B、根桥是SWB C、根桥是SWC D、根桥无法确定 参考答案:A 第502题 在华为设备中,以下哪一个命令可以实现BFD与静态默认路由联动? A、ip route-static 0.…

嵌入式开发——面试题操作系统(调度算法)

linux7种进程调度算法 1&#xff1a;先来先服务&#xff08;FCFS&#xff09;调度算法 原理&#xff1a;按照进程进入就绪队列的先后次序进行选择。对于进程调度来说&#xff0c;一旦一个进程得到处理机会&#xff0c;它就一直运行下去&#xff0c;直到该进程完成任务或者因等…

“祖传代码”:程序员的“宝藏图”还是“地雷区”?

程序员对“祖传代码”的看法可能因个人经验、技能和项目需求等因素而有所不同。但无论如何&#xff0c;祖传代码的背后都有一段段啼笑皆非或者令人深省的故事。 一、程序员对祖传代码的看法 结合笔者以及身边形形色色大猿小猿的态度&#xff0c;浅浅罗列以下看法: 恐惧和厌恶…

day03_登录注销(前端接入登录,异常处理, 图片验证码,获取用户信息接口,退出功能)

文章目录 1. 前端接入登录1.1 修改前端代码1.2 跨域请求1.2.1 跨域请求简介1.2.2 COSR概述CORS简介CORS原理 1.2.3 CORS解决跨域 2. 异常处理2.1 提示空消息分析2.2 系统异常分类2.3 异常处理2.2.1 方案一2.2.2 方案二 3. 图片验证码3.1 图片验证码意义3.2 实现思路3.3 后端接口…

微服务 人工智能AI 物联网智慧工地云平台源码

目录 ​编辑 智慧工地架构 智慧工地系统 智慧工地云平台功能模块 1、基础数据管理 2、考勤管理 3、安全隐患管理 4、视频监控 5、塔吊监控 6、升降机监控 7、移动端数据推送 智慧工地管理平台子系统构成 智慧工地物联网解决方案&#xff0c;对工地施工安全人员、设…

三种食物轮流吃,睡眠时间又长又香!

睡眠质量一直是人们关注的焦点&#xff0c;而饮食则被认为是影响睡眠的重要因素之一。近年来&#xff0c;有一种食物搭配方法备受瞩目&#xff0c;据说可以让人们的睡眠时间又长又香。这种方法并不复杂&#xff0c;只需要轮流食用三种特定食物&#xff0c;就能有效改善睡眠质量…

window server 2012 r2配置多用户远程登录

window server2012r2配置多用户远程登录 注&#xff1a;window系统默认只允许一个用户登录&#xff0c;但在配置中可以设置多用户同时远程。非特殊情况不建议设置多用户远程&#xff0c;以防数据丢失&#xff0c;或数据篡改无法排查。 1、桌面winr打开gpedit.msc 2、点击管理…

BigTime 2024:多人对战新期待,链游迎来强势回归

随着2024年加密货币市场行情回暖&#xff0c;区块链游戏正成为行业中充满活力的领域。截至2023年&#xff0c;该市场的价值已超过30亿美元&#xff0c;并预计到2030年将达到900亿美元。这种增长部分源于投资的增加和NFT的广泛应用。同时&#xff0c;融合传统游戏元素与区块链技…

简单网站模板1(HTML)

想要拥有自己的网站&#xff0c;却不知该如何才能简约好看&#xff0c;接下来分享一种自己搭建的网站模板&#xff0c;希望大家喜欢。 展示图&#xff1a; CODE: <!DOCTYPE html> <html> <head><title>我的网站</title><style>body {fo…

如何开展有效的绩效面谈

绩效面谈作为绩效管理的核心环节&#xff0c;其重要性不容忽视。它不仅是评价员工过去一段时间工作表现的环节&#xff0c;更是为下一阶段绩效管理设定目标和方向的环节。然而&#xff0c;许多企业在实施绩效面谈时&#xff0c;往往仅停留在形式上&#xff0c;没有真正地发挥其…

分享:大数据信用报告查询的价格一般要多少钱?

现在很多人都开始了解自己的大数据信用了&#xff0c;纷纷去查大数据信用报告&#xff0c;由于大数据信用与人行征信有本质的区别&#xff0c;查询方式和价格都不是固定的&#xff0c;本文就为大家详细讲讲大数据信用报告查询的价格一般要多少钱&#xff0c;希望对你有帮助。 大…

windows上elasticsearch的ik分词器的安装

下载 下载地址 在elasticsearch下的plugins文件夹下创建ik的文件夹 下载的ik压缩包解压到plugins/ik 重启elasticsearch 验证 http://ip:9200/_cat/plugins

2024可持续发展与电力系统、能源国际会议(ICSDPSE 2024)

2024可持续发展与电力系统、能源国际会议&#xff08;ICSDPSE 2024&#xff09; 一、【会议简介】 非常高兴邀请您参加2024年可持续发展与电力系统、能源国际会议&#xff08;ICSDPSE 2024&#xff09;。该会议将于2024年在苏州举行&#xff0c;这是一个旨在促进可持续发展和…

log4j 基础使用入门教程

一、Log4j介绍 在项目中&#xff0c;不管是开发人员写代码还是测试人员写的测试代码一般都需要做一些日志来记录项目的行为&#xff0c;以便更好的跟踪项目中的一些交互和问题。 Log4j ( Logger For Java ) , Java 日志的记录包。 官方网站 。Log4j 是 Apache 的一个开源项目…

虚拟机内存不够用了?全流程操作Look一下?

虚拟机信息&#xff1a;操作系统&#xff1a;CentOS Linux 7 (Core)&#xff0c;用的是VMware Workstation 16 Pro 版本16.2.3 build-19376536&#xff1b;我的主机 Windows 10 Education, 64-bit (Build 22000.1817) 10.0.22000 前言&#xff1a;虚拟机用久了就会出现内存不足…

JVM之CMS垃圾收集器详解

CMS垃圾收集器 CMS回收流程 官网&#xff1a; https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html#concurrent_mark_sweep_cms_collector CMS(Concurrent Mark Sweep)收集器是一种以获取 最短回收停顿时间为目标的收集器。 采用的是"标记-清除…

【精选】Java项目介绍和界面搭建——拼图小游戏 上

&#x1f36c; 博主介绍&#x1f468;‍&#x1f393; 博主介绍&#xff1a;大家好&#xff0c;我是 hacker-routing &#xff0c;很高兴认识大家~ ✨主攻领域&#xff1a;【渗透领域】【应急响应】 【Java】 【VulnHub靶场复现】【面试分析】 &#x1f389;点赞➕评论➕收藏 …

【三维重建】【slam】【分块重建】LocalRF:逐步优化的局部辐射场的鲁棒视图合成

项目地址&#xff1a;https://localrf.github.io/ 题目&#xff1a;Progressively Optimized Local Radiance Fields for Robust View Synthesis 来源&#xff1a;KAIST、National Taiwan University、Meta 、University of Maryland, College Park 提示&#xff1a;文章用了s…