【题解】NOIP普及组2019

用了大概一个晚自习来做这四道题,顺便复习一下以前学过的知识。

A. 数字游戏

签到题。。。

#include <iostream> using namespace std; string s; int ans; int main() { cin >> s; for (int i = 0; i < s.size(); i++) if (s[i] == '1') ans++; cout << ans; }

B.公交换乘

按题意模拟即可。

  1. 若type==0,累加花费,从优先队列中压入二元组(x,y),x表示花费,y表示时间。
  2. 若type==1,首先去掉超时的项。然后一直top,直到q.top().x>=x。将之前top的又push回去(不包括当前选择的项)。
  3. 输出ans。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 5; struct node { int x, y; friend bool operator<(node a, node b) { return a.y > b.y; } node() {} node(int X, int Y) { x = X, y = Y; } }; int n, ans, tot; priority_queue<node> q; int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { int x, y, z; scanf("%d%d%d", &x, &y, &z); if (!x) { ans += y; q.push(node(y, z)); } else { bool flag = 0; queue<node> p; while (q.size() && z - q.top().y > 45) q.pop(); while (q.size()) { node t = q.top(); q.pop(); if (t.x >= y) { flag = 1; break; } p.push(t); } if (!flag) ans += y; while (p.size()) { q.push(p.front()); p.pop(); } } } printf("%d", ans); }

C.纪念品

本题笔者并没有做出来。。。

分析:
正解是背包。
背包问题最重要的是弄清楚价值和背包容量。首先理解题意,假如要记录所有当前已买纪念品,显然是不可能的。

所以,当天买的纪念品,第二天必须全部卖掉。为什么呢?假设你在第i天买了t个物品j,价值是price[i][j],第二天则变成了price[i+1][j]。此时保留这t个纪念品,等价于先以price[i+1][j]卖掉,再以price[i+1][j]买回来,手中的钱不变。

结论:在第i天,拥有纪念品和拥有它的价值是一样的。

就好像炒股,只不过没有了买入卖出的手续费。

所以,背包就是第i天的钱数,买物品j的价值就是price[i+1][j]-price[i][j](这里的价值是买它所获得的利润),j的体积就是price[i][j].

d p [ k ] = m a x ( d p [ k ] , d p [ k − p r i c e [ i ] [ j ] ] + p r i c e [ i + 1 ] [ j ] − p r i c e [ i ] [ j ] ) dp[k]=max(dp[k],dp[k-price[i][j]]+price[i+1][j]-price[i][j]) dp[k]=max(dp[k],dp[kprice[i][j]]+price[i+1][j]price[i][j])

我们把每天都额外加上前一天的ans元钱能获得的最大利润,重复t天,就得到了答案。

#include<bits/stdc++.h> using namespace std; const int N=105; const int M=10005; int t,n,m,cost[N][N],dp[M],ans; int main() { scanf("%d%d%d",&t,&n,&m); for(int i=1;i<=t;i++) for(int j=1;j<=n;j++) scanf("%d",&cost[i][j]); ans=m; for(int i=1;i<t;i++) { memset(dp,0,sizeof(dp)); for(int j=1;j<=n;j++) { for(int k=cost[i][j];k<=ans;k++) { dp[k]=max(dp[k],dp[k-cost[i][j]]+cost[i+1][j]-cost[i][j]); } } ans+=dp[ans]; } printf("%d",ans); }

这道题也不是特别难(码量不大),但一定要弄明白每个量的含义,以及容量、价值和体积。

说白了就是忘了背包怎么做了

D.加工零件

感觉比上一道好想点。

分析:
本题的关键在于:你不能照搬L(L<=1e9),假如从(a,l)开始搜索,一直搜到(1,0),一定会超时。

一句话题意:问是否有从1到a的长度为L的路径。

自然而然想到最短路。因为路径可逆,所以选择1为起点。假设dis[a]>L,显然不可能;若dis[a]<=L,我们选择分析奇偶性。因为从a到a一定存在长度为2的路径。

然后拆点:

for(int i=1,x,y;i<=m;i++) { //奇偶性相同的连边 scanf("%d%d",&x,&y); son[x].push_back(y+n); son[y+n].push_back(x); son[y].push_back(x+n); son[x+n].push_back(y); }

注意光判断奇偶性还不行,必须当最短路<=L才行。假设有两个非常远的点,那么L太小是一定传递不过去的。

ac代码:

#include<bits/stdc++.h> using namespace std; const int N=2e5+5; int n,m,q,dis[N]; vector<int> son[N]; void dijkstra() { memset(dis,0x3f,sizeof(dis)); queue<int> q; q.push(1); dis[1]=0; while(q.size()) { int x=q.front();q.pop(); for(int i=0;i<son[x].size();i++) { int y=son[x][i]; if(dis[x]+1<dis[y]) { dis[y]=dis[x]+1; q.push(y); } } } } int main() { scanf("%d%d%d",&n,&m,&q); for(int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); son[x].push_back(y+n); son[y+n].push_back(x); son[y].push_back(x+n); son[x+n].push_back(y); } dijkstra(); for(int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); //若y为奇数,则应该找x+n到1的路径长度 if((y%2==0&&dis[x]<=y)||(y%2==1&&dis[x+n]<=y)) printf("Yes\n"); else printf("No\n"); } }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530402.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(40)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
历史上的今天:
2022-01-06 考虑费用流若干性质
2022-01-06 关于消圈算法的正确姿势
2022-01-06 【学习笔记】dij 费用流 + 势能函数
点击右上角即可分享
微信分享提示