NOIP2011 复盘

D1T1 P1003 铺地毯

经典题目,不必多说

#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 10005;
int a[maxn], b[maxn], g[maxn], k[maxn], n;
int x, y;
int main() {scanf("%d", &n);for(int i = 1; i <= n; i++) scanf("%d %d %d %d", &a[i], &b[i], &g[i], &k[i]);scanf("%d %d", &x, &y);for(int i = n; i >= 1; i--) {if(a[i] <= x && x <= a[i] + g[i] && b[i] <= y && y <= b[i] + k[i]) {printf("%d\n", i);return 0;}}printf("-1\n");return 0;
}

D1T2 P1311 选择客栈

对于最低消费,套一个ST表求RMQ。

我们将相同的色调的点一起存,然后考虑用暴力跑出答案。

然后可以发现,如果左端点固定,只需要按顺序向右找第一个满足条件的点,后面的点来组成一定都可以。

然后不知道为什么就过了。。。数据水

急需学习新解法!

#include<bits/stdc++.h>
const int maxn = 200005;
int n, k, p;
int c[maxn], a[maxn];
int st[maxn][21];
std::vector<int> colors[55];void init() {for(int i = 1; i <= n; i++) st[i][0] = a[i];for(int j = 1; j <= 20; j++) {for(int i = 1; i + (1 << j) - 1 <= n; i++) {st[i][j] = std::min(st[i][j - 1], st[i + (1 << (j - 1))][j - 1]);}}
}
int query(int l, int r) {int k = 0;while((1 << (k + 1)) < r - l + 1) k++;return std::min(st[l][k], st[r - (1 << k) + 1][k]);
}
int main() {scanf("%d %d %d", &n, &k, &p);for(int i = 1; i <= n; i++) {scanf("%d %d", &c[i], &a[i]);colors[c[i]].push_back(i);}init();int ans = 0;for(int i = 0; i < k; i++) {for(int j = 0; j < colors[i].size(); j++) {for(int k = j + 1; k < colors[i].size(); k++) {if(query(colors[i][j], colors[i][k]) <= p) {ans += colors[i].size() - k;break;}}}}printf("%d\n", ans);return 0;
}

题解里面有一个解法十分简洁:

#include <iostream>
#define maxn 200005
using namespace std;
int n,k,p;
int color,price;
int last[maxn];
int sum[maxn];
int cnt[maxn];
int ans = 0;
int now;
int main(){cin >> n >> k >> p;for (int i=1;i<=n;i++){cin >> color >> price;if (price <= p)now = i;if (now >= last[color])sum[color] = cnt[color];last[color] = i;ans += sum[color];cnt[color]++;}cout << ans << endl;return 0;
}

代码通过记录前面有多少与之同色调而且包含了可以住下的now的点数,只要一次遍历就可以解决。nb!

D1T3 P1312 Mayan游戏

这道题是真的难,既难骗分又难写正解。。

显然我们一定要套dfs的思路,在固定搜\(n\)步中搜到所有方块都被消掉。

可以发现,这道题的重要动作是:交换两个方块,悬浮方块掉下,同色方块消除,悬浮方块掉下,同色方块消除,……

所以我们大的方向是:每一步搜交换哪两个方块,然后掉落,接下来进入循环,若能消就消,然后继续掉落,若不能消则一次操作结束。

我们接下来考虑如何写掉落函数:我自己有一种递推的方法可以计算出悬浮方块的掉落后位置。

dp[i][j]为悬浮方块中\(i\)行\(j\)列将会掉到的行数(从下开始计数),若下面刚好为空,则暴力找到掉下的位置,正上面的方块只需要继承这个dp值然后再加1就可以了。

代码(抄题解的)中的掉落更聪明,直接用一个变量维护了,可以参考下。

消除是个难点,因为还有图5那种莫名其妙很难实现的消除,我们看看能够怎么解决:(注意:这个函数只代表一次消除,你可能要消除很多遍)

我们枚举一个点,如果左右都有同色的那横的就能消除,再暴力探索最左和最右同色的色块,都打上标记。上下消除的也是同理。最后将所有打过标记的都消除,再让他们掉落就是了。

如果整个状态找了一遍都没有出现上述任何一次三个以上的,就是没能消,一次操作就结束。

但是如果直接这么写还过不了。在交换的时候有很多的浪费,可以剪枝:

  1. 交换两个相同的色块没有意义。
  2. 左边的主动跟右边交换比右边主动跟左边交换更优,所以不往左边交换,除非左边为空才向左移。

代码:

/*************************************************************************@Author: Garen@Created Time : Wed 30 Jan 2019 12:53:57 PM CST@File Name: P1312.cpp@Description:************************************************************************/
#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
const int maxn = 6, maxm = 8;
int G[maxn][maxm];
int backup[maxn][maxn][maxm];
struct Nodes {int x, y, g;Nodes(int x, int y, int g): x(x), y(y), g(g) {}
};
std::vector<Nodes> answers;
bool vis[maxn][maxm];
int n;void copy_one(int t) {for(int i = 1; i <= 5; i++) for(int j = 1; j <= 7; j++) backup[t][i][j] = G[i][j];
}
void copy_two(int t) {for(int i = 1; i <= 5; i++) for(int j = 1; j <= 7; j++) G[i][j] = backup[t][i][j];
}
bool find() {bool flag = false;for(int i = 1; i <= 5; i++) {for(int j = 1; j <= 7; j++) {if(G[i][j] && i - 1 >= 1 && i + 1 <= 5 && G[i][j] == G[i + 1][j] && G[i][j] == G[i - 1][j]) {flag = true;vis[i][j] = vis[i - 1][j] = vis[i + 1][j] = true;for(int k = i - 2; k >= 1; k--) {if(G[i][j] == G[k][j]) vis[k][j] = true;else break;}for(int k = i + 2; k <= 5; k++) {if(G[i][j] == G[k][j]) vis[k][j] = true;else break;}}if(G[i][j] && j - 1 >= 1 && j + 1 <= 7 && G[i][j] == G[i][j - 1] && G[i][j] == G[i][j + 1]) {flag = true;vis[i][j] = vis[i][j - 1] = vis[i][j + 1] = true;for(int k = j - 2; k >= 1; k--) {if(G[i][j] == G[i][k]) vis[i][k] = true;else break;}for(int k = j + 2; k <= 7; k++) {if(G[i][j] == G[i][k]) vis[i][k] = true;else break;}}}}if(!flag) return false;for(int i = 1; i <= 5; i++) {for(int j = 1; j <= 7; j++) {if(vis[i][j]) {vis[i][j] = G[i][j] = 0;}}}return true;
}
void update() {for(int i = 1; i <= 5; i++) {int now = 0;for(int j = 1; j <= 7; j++) {if(!G[i][j]) now++;else {if(!now) continue;G[i][j - now] = G[i][j]; G[i][j] = 0;}}}
}
bool empty() {for(int i = 1; i <= 5; i++) if(G[i][1]) return false;return true;
}
void dfs(int t) {if(t == n) {if(empty()) {for(auto it : answers) cout << it.x << ' ' << it.y << ' ' << it.g << endl;exit(0);}return;}copy_one(t);for(int i = 1; i <= 5; i++) {for(int j = 1; j <= 7; j++) {if(G[i][j]) {// rightif(i + 1 <= 5 && G[i][j] != G[i + 1][j]) {std::swap(G[i][j], G[i + 1][j]);answers.push_back(Nodes(i - 1, j - 1, 1));update();while(find()) update();dfs(t + 1);copy_two(t);answers.pop_back();}// leftif(i - 1 >= 1 && G[i - 1][j] == 0) {std::swap(G[i][j], G[i - 1][j]);answers.push_back(Nodes(i - 1, j - 1, -1));update();while(find()) update();dfs(t + 1);copy_two(t);answers.pop_back();}}}}
}
int main() {cin >> n;for(int i = 1; i <= 5; i++) {int temp;for(int j = 1; ; j++) {cin >> temp;if(temp) G[i][j] = temp;else break;}}dfs(0);cout << -1 << endl;return 0;
}

D2T1 P1313 计算系数

这道题是数学题,算就完事了。

根据2-3学的二项式定理,就能够发现\(x^n y^m\)项的系数就是\(C_k^n b^m a^n\)。

因为数据都很小,直接用ksm和暴力组合数算法就能求出答案了。

D2T2 P1314 聪明的质监员

经典的二分题。

显然,\(W\)越大\(Y\)越小,\(W\)越小\(Y\)越大,要想\(Y\)在\(S\)附近,就必须控制\(W\)在一定区间范围。

所以我们直接二分\(W\),算出当前\(W\)下对应的\(Y\),若大了就增大\(W\),若小了就减少,这样就会越来越精确。

但是如何高效地求出对应的\(Y\)值?我们用两个前缀和维护两个\(\sum\)的值,\(O(n)\)滚一遍之后每个\(Y_i\)就都可以\(O(1)\)求出来了。

所以复杂度是完美的\(O(n \log n)\)。

#include<bits/stdc++.h>
using std::cin;
using std::cout;
using std::endl;
#define ll long long
const ll maxn = 200005;
ll w[maxn], v[maxn];
ll L[maxn], R[maxn];
ll n, m, S;
ll a[maxn], b[maxn];
ll Y;
bool check(ll mid) {a[0] = b[0] = 0;for(int i = 1; i <= n; i++) {a[i] = a[i - 1] + (w[i] >= mid);b[i] = b[i - 1] + (w[i] >= mid ? v[i] : 0);}Y = 0;for(int i = 1; i <= m; i++) {Y += (a[R[i]] - a[L[i] - 1]) * (b[R[i]] - b[L[i] - 1]);}//cout << mid << ": " << res << endl;if(S - Y >= 0) {return true;}else return false;
}
int main() {cin >> n >> m >> S;ll left = 1e16, right = 0;for(int i = 1; i <= n; i++) {cin >> w[i] >> v[i];left = std::min(left, w[i]); right = std::max(right, w[i]);}for(int i = 1; i <= m; i++) cin >> L[i] >> R[i];ll ans = 0x3f3f3f3f3f3f3f3f;left -= 1; right += 2;while(left < right) {ll mid = (left + right) / 2;if(check(mid)) right = mid;else left = mid + 1;ans = std::min(ans, llabs(S - Y));}//cout << abs(S - ANS) << endl;cout << ans << endl;return 0;
}

D2T3 P1315 观光公交

个人错误:把\(k\)放在一起考虑,无法做到真正的贪心

这是一道不一样的贪心。。。

首先给出数据范围:\(n \leq 1000,m \leq 10000,k \leq 100000\)。

题目的模拟部分非常简单,就是普通的上车和下车,将所有人所用的时间相加即可得到答案。

不难发现成功的模拟用到的复杂度是\(O(n)\)的。

部分分给了我们提示,当\(k=1\)时,我们一定会选择那条经过人最多的路。

但是\(k>1\)时,很难协调每个方案之间的关系。

其实,只要把每一次加速器当做最后一次加速器,每次找到加速一次的最佳路径,重复做\(k\)遍即可。 可以发现这样做一定是最优的。

为什么\(k\)复杂度那么大还能重复做\(k\)遍啊?因为\(O(kn)\)没有超时!

题解里面用到了一个range数组,挺有精髓的,这里解释一下:

range[i]代表当前状况,如果在第\(i\)条路用了加速器,会从\(i+1\)影响到哪里。

计算range[i]采用了倒推,如果一个站的时候车不用等人,就直接继承后面,否则就到此为止。

代码很有细节:

#include<bits/stdc++.h>
const int maxn = 10005;
const int INF = 0x3f3f3f3f;
int n, m, k;
struct Nodes {int tim, s, t;
} s[maxn];
int latest[maxn];
int tim[maxn];
int range[maxn];
int ans;
int d[maxn];
int sum[maxn];
int main() {scanf("%d %d %d", &n, &m, &k);for(int i = 1; i < n; i++) scanf("%d", &d[i]);for(int i = 1; i <= m; i++) {scanf("%d %d %d", &s[i].tim, &s[i].s, &s[i].t);latest[s[i].s] = std::max(latest[s[i].s], s[i].tim);sum[s[i].t]++;}for(int i = 1; i <= n; i++) sum[i] += sum[i - 1];for(int i = 1; i < n; i++) {tim[i + 1] = std::max(tim[i], latest[i]) + d[i];}for(int i = 1; i <= m; i++) {ans += tim[s[i].t] - s[i].tim;}while(k--) {int maxv = 0, idx = -1;range[n - 1] = n;for(int i = n - 2; i >= 1; i--) {if(tim[i + 1] > latest[i + 1]) range[i] = range[i + 1];else range[i] = i + 1;}for(int i = 1; i < n; i++) {if(sum[range[i]] - sum[i] > maxv && d[i]) {maxv = sum[range[i]] - sum[i]; idx = i;}}if(maxv == 0) break;ans -= maxv;d[idx]--;tim[1] = 0;for(int i = 1; i < n; i++) {tim[i + 1] = std::max(tim[i], latest[i]) + d[i];}}printf("%d\n", ans);return 0;
}

转载于:https://www.cnblogs.com/Garen-Wang/p/11333401.html

NOIP2011 复盘相关推荐

  1. 复盘一次服务安装失败问题

    目录 前言 问题描述 问题分析 日志分析 重启失败服务 解决方案 总结 前言 记录一下本周服务改造过程踩坑经历.近期在做服务改造接入统一配置中心,对一些早期的服务进行升级,由此带来了不少问题,好在都在 ...

  2. AI视频行为分析系统项目复盘——技术篇4:deepsort原理图

    0 背景 见<AI视频行为分析系统项目复盘--技术篇1> 1 目标 尽力绘制详尽的原理图,弄懂deepsort的全局和细节,希望具备以下能力: 精通--能够魔改算法. 一叶知秋--深入了解 ...

  3. AI视频行为分析系统项目复盘——技术篇3:tensorRT技术梳理

    0 背景 见<AI视频行为分析系统项目复盘--技术篇1> 1 tensorRT 介绍 NVIDIA®TensorRT™是一个深度学习平台,用于模型推理加速(仅支持NVIDIA自家GPU,C ...

  4. AI视频行为分析系统项目复盘——技术篇2:视频流GPU硬解码

    0 项目背景 见<AI视频行为分析系统项目复盘--技术篇1> https://blog.csdn.net/weixin_42118657/article/details/118105545 ...

  5. 【第20周复盘】转换思路,让更多的小朋友们参与进来!

    「青少年编程竞赛交流群」已成立(适合6至18周岁的青少年),公众号后台回复[Scratch]或[Python],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载&quo ...

  6. 【第13周复盘】小朋友们也开始卷了

    「青少年编程竞赛交流群」已成立(适合6至18周岁的青少年),公众号后台回复[Scratch]或[Python],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载&quo ...

  7. 【第11周复盘】小朋友们 100% 闯关成功!

    「青少年编程竞赛交流群」已成立(适合6至18周岁的青少年),公众号后台回复[Scratch]或[Python],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载&quo ...

  8. 【复盘】小朋友的奇思妙想

    Scratch竞赛交流群已成立(适合6至18周岁的青少年),公众号后台回复[Scratch],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载"可获取以往学习 ...

  9. 【复盘】第一次灌鸡汤

    Scratch竞赛交流群已成立(适合6至18周岁的青少年),公众号后台回复[Scratch],即可进入.如果加入了之前的社群不需要重复加入. 微信后台回复"资料下载"可获取以往学习 ...

最新文章

  1. 开放式神经网络交换-ONNX(上)
  2. 3月9日起陆续开学!快来看看有没有你所在的省份
  3. 社区活跃吗_武汉大江园社区活跃着一支90后志愿者突击队
  4. 2021年度人工智能产品TOP10,百度飞桨EasyDL再获业界认可
  5. perl语言入门第七版中文_网站编程语言的选择
  6. 用ajax的方法获取列表,用ajax从服务器获取列表数据,为什么getElementsByTaName('li')的长度为0呢...
  7. 关于sscanf与结构体不能联用的问题
  8. 水题:P2799 国王的魔镜
  9. 我们生活在最好的时代
  10. ADO RecondsetPtr 以及如何实现对表的增加删除数据
  11. 汉堡王,你不要给我们AI泼脏水
  12. SQL SERVER BCP的用法
  13. UNet多类别分割的keras实现
  14. TBSchedule源码学习笔记-启动过程
  15. 使用Simian进行重复代码检测
  16. 移动硬盘在电脑上显示为本地磁盘并且出现打不开的情况
  17. maven日记(一):Maven使用入门
  18. 不应该通过类实例来访问静态成员
  19. Solaris中文FAQ
  20. Verilog初级教程(2)Verilog HDL的初级语法

热门文章

  1. 使用TortoiseGit导出GIT分支差异文件
  2. 890X系列MBN OTA配置方式
  3. ​在 Android 上进行高刷新率渲染
  4. 电子邮箱地址是什么?电子邮箱地址怎么写?电子邮箱地址在哪里找?
  5. Jdk8新特性三:jdk8之函数式编程Function
  6. C++中atoi()函数的用法
  7. 基于AIOT技术的智慧教室智能物联管控系统设计与实现(提纲)
  8. 如何提高网页的效率(下篇)——Use YSlow to know why your web Slow
  9. 用python将十进制数转换成二进制数_python中的数据结构-将十进制数转换为二进制数...
  10. XXE漏洞漏洞及利用方法解析