2019 ICPC 银川题解(A,H,L)

赛时没发挥好6题金尾(rank38),剩下很多能写的题,其中四个dp,傻眼ing
The 2019 ICPC Asia Yinchuan Regional Contest

A Girls Band Party(背包)

有点迷惑的题,当时看只要 5 5 5 张牌一下子想到暴力枚举,结果发现是不太能行的,导致浪费很多时间。

题意

n n n 张牌,每张牌有 名称,颜色,价值。再给定 5 5 5 个特殊的牌的名称,和一个特殊的牌的颜色。要求从 n n n 张牌中选出 5 5 5 张名称不同的卡牌,基础分数为 5 5 5 张牌的价值之和 s u m sum sum,在此基础上每多一张牌是特殊颜色最终价值加上 0.2 ∗ s u m 0.2*sum 0.2sum,每多一张特殊名字牌最终价值加上 0.1 ∗ s u m 0.1*sum 0.1sum,最终价值向下取整,求选出的卡牌最大可能的价值。

思路

对于复杂的题目我们先从简单的问题开始考虑。

若是对于该问题只考虑价值之和不考虑附加价值和不能同名的问题是否能解决?就是一个简单的背包,容量为 5 5 5 求最大值的问题。

接下来增加新的信息,若是要求 5 5 5 张牌不能重名如何解决?也很简单,分组背包,同名的牌视作一组,一组内的物品只能选一样的背包问题

再增加新的信息,普通颜色和普通名称对答案没有任何影响,特殊颜色和名称也只有数量对答案有影响,和具体是什么无关。我们是否能在背包时顺便统计特殊颜色和特殊名称?,也很简单,因为只要选出 5 5 5 张牌,将特殊颜色和特殊名称也当做价值的一种算进dp方程,给两个各多开一维维护即可。

到此问题已经顺利解决,给出dp方程: d p [ i ] [ j ] [ k ] [ p ] : dp[i][j][k][p]: dp[i][j][k][p] i i i 张卡牌,选出 j j j 张卡牌, k k k 张是特殊颜色, p p p 张是特殊名字卡牌的 合法方案中的最大基数价值。

具体实现可以看代码,有注释和解析。

#include <bits/stdc++.h>
using namespace std;#define ll long longconst int N = 1e5 + 10;int n;string s[10], color;
map<string, bool> mp;struct node{string d, c;int val;
}cd[N];bool cmp(node& A, node& B){return A.d < B.d;
}int f[N][6][6][6]; // 前 i 张卡牌,选出 j 张卡牌,k 张是特殊颜色,p 张是特殊名字卡牌的 最大基数价值void cmax(int& a, int b){ a = max(a, b); }
void solve(){int n;cin >> n;for(int i = 1; i <= n; i ++){cin >> cd[i].d >> cd[i].c >> cd[i].val;}mp.clear();for(int i = 0; i < 5; i ++){cin >> s[i];mp[s[i]] = 1;}cin >> color;sort(cd + 1, cd + 1 + n, cmp);  // 按名称排序,方便进行类似分组背包的dpfor(int i = 1; i <= n; i ++){for(int j = 0; j < 6; j ++){for(int k = 0; k < 6; k ++){for(int p = 0; p < 6; p ++) f[i][j][k][p] = 0;}}}int last = 0; // 记录最近的上一个不同名的卡牌为止for(int i = 1; i <= n; i ++){if(cd[i].d != cd[i - 1].d) last = i - 1; int c_v = (cd[i].c == color), s_v = mp.count(cd[i].d); // 这张牌是否是特殊颜色和名称for(int j = 0; j <= 5; j ++){for(int k = 0; k <= j; k ++){for(int p = 0; p <= j; p ++){// 不取这张牌cmax(f[i][j][k][p], f[i - 1][j][k][p]); // 可以从同名的卡牌转移 也可以从不同名的转移// 取这张牌if((j != 0 && f[last][j][k][p] == 0) || j == 5) continue ; // j = 5 时注意不要越界了cmax(f[i][j + 1][k + c_v][p + s_v], f[last][j][k][p] + cd[i].val); // 取这一张牌,那么只能从不同名的卡牌转移}}}}ll ans = 0;for(int i = 0; i <= 5; i ++){ // 枚举特殊颜色数量for(int j = 0; j <= 5; j ++){ // 枚举特殊名字数量double mul = 0.2 * (double)i + 0.1 * (double)j; // 系数ans = max(ans, (ll)(mul * (double)f[n][5][i][j]) + f[n][5][i][j]);}}cout << ans << "\n";
}int main(){ ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int t;cin >> t;while(t --){solve();}return 0;
}

H Delivery Route(最短路+拓扑排序)

题意

给定一个有向图 G G G x x x 条双向边 y y y 条单向边,可能有负边(保证是单向边)但保证走过单向边 u → v u\rightarrow v uv 后一定走不回 u u u,求起点 s s s 到所有点的最短路,如果到不了输出 “NO PATH”.

思路

乍一看仿佛很简单,跑个迪杰斯特拉(后文简称djs)最短路不就行了吗?但是写完准备交的时候就发现有坑,因为djs算法是每次从优先队列取出目前为止到起点最近的点 u u u 进行扩展,但是这样的扩展不一定是最优的,但之前扩展过一次下次再想用该点 u u u 扩展就不行了。
考虑这样一个图: 1 1 1 是起点
在这里插入图片描述

1 → 2 → 4 → 5 1\rightarrow2\rightarrow4\rightarrow5 1245 显然是第一轮扩展,在这几个点取出队列之前队首都不会轮到 3 3 3,因为此时 d i s [ 3 ] = 2 > d i s [ 2 ] = 1 > d i s [ 4 ] = 0 > d i s [ 5 ] = 1 dis[3] = 2 > dis[2] = 1 > dis[4] = 0 > dis[5] = 1 dis[3]=2>dis[2]=1>dis[4]=0>dis[5]=1,因为此时这几个点都已经作为过一次扩展点了,之后就不能再次扩展,但是发现当最后点 3 3 3 进行扩展时, 3 → 4 → 5 3\rightarrow4\rightarrow5 345 会更优秀。

由于题目保证没有负环,我们考虑使用拓扑排序进行优化,先将双向边连接起的连通块用并查集合并,再将单向边连接的这些连通块点集度数 + 1 + 1 +1但要注意若是有单向边 u → v u\rightarrow v uv,且 u u u 点是不可从起点到达的,就不用记录该度数。

接下来就是普通的djs算法 + 拓扑排序了,只有当度数为 0 0 0 时才将一个连通块加入优先队列。具体见代码和注释。

#include <bits/stdc++.h>
using namespace std;#define ll long long
typedef pair<ll, int> pli;const int N = 25010;
const ll inf = 1e18;vector<pair<int, int> > g[N], e[N];int n, m1, m2, s;priority_queue<pli, vector<pli>, greater<pli>> q;ll dis[N];
bool vis[N];struct DSU {std::vector<int> f, siz;DSU() {}DSU(int maxn) {init(maxn);}void init(int maxn) {f.resize(++ maxn); // 重构容器大小到 nstd::iota(f.begin(), f.end(), 0); // 批量递增赋值// siz.assign(maxn, 1); // 赋值n个1}int find(int x) {while (x != f[x]) {x = f[x] = f[f[x]];}return x;}bool same(int x, int y) {return find(x) == find(y);}bool merge(int x, int y) {x = find(x);y = find(y);if (x == y) {return false;}f[y] = x;return true;}
};DSU dsu;
int d[N];
vector<int> ID[N];
void djs_top(){for(int i = 1; i <= n; i ++) dis[i] = inf, vis[i] = 0;dis[s] = 0;q.push({0, s});while(!q.empty()){auto [di, u] = q.top(); q.pop();if(vis[u]) continue ;vis[u] = 1;// djsfor(auto [v, w] : g[u]){if(dis[v] > dis[u] + w){dis[v] = dis[u] + w;q.push({dis[v], v});}}// 拓扑排序for(auto [v, w] : e[u]){ // 单向边int id = dsu.find(v);d[id] --;if(dis[v] > dis[u] + w) dis[v] = dis[u] + w;ID[id].push_back(v); // 存入连通块if(!d[id]){ // 当连通块度数为0,再将所有块内之前出现过的点存入队列for(auto x : ID[id]) q.push({dis[x], x});}}}
}void dfs(int u){if(vis[u]) return ;vis[u] = 1;for(auto [v, w] : g[u]) dfs(v);for(auto [v, w] : e[u]) dfs(v);
}int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);cin >> n >> m1 >> m2 >> s;dsu.init(n);for(int i = 1; i <= m1; i ++){int u, v, w;cin >> u >> v >> w;dsu.merge(u, v); // 并查集合并g[u].push_back({v, w});g[v].push_back({u, w});}for(int i = 1; i <= m2; i ++){int u, v, w;cin >> u >> v >> w;e[u].push_back({v, w});}dfs(s); // 将不可达的点判除for(int i = 1; i <= n; i ++){if(!vis[i]) continue ;for(auto [v, w] : e[i]) d[dsu.find(v)] ++; // 连通块的度数 ++}djs_top();for(int i = 1; i <= n; i ++){if(!vis[i]) cout << "NO PATH\n";else cout << dis[i] << "\n";}return 0;
}

L Xian Xiang

思路简单,处理很麻烦的一道题,差点做吐了。
在这里插入图片描述

题意

每次给定 n ∗ m n*m nm 的矩阵,每个单位代表一个对象(以长度为 k k k 的字符串的形式给出),每次可以删除两个对象(类似连连看,但要求连线必须水平或垂直且最多只能改变一次方向)且路径上不能有未删除的对象(可以有空对象)。
删除两个对象的价值为对应位置上字符相同的数目对应的价值,若相同字符数目为 i i i,价值为 s i s_i si. 题目保证非空对象最多为 18 18 18 个,且为偶数,求可能删除方案的最大价值。

思路

很简单就能想到状压dp,每次枚举两个未删除的非空对象判断是否能连线删除即可,能删除就能转移。难的是处理起来很麻烦,并且题目有 T T T 组样例,要求转移的判断常数要比较小才行。

设检查函数的时间复杂度为 O ( k ) O(k) O(k),状压复杂度 2 18 2^{18} 218,但删除必然是两个一删除,降一阶为 2 17 2^{17} 217,每次需要枚举未删除的两个数,所以总复杂度为 O ( 3407872 ∗ T ∗ k ) O(3407872*T*k) O(3407872Tk).

计算复杂度代码:

int main(){int cnt = 0;for(int i = 0; i < (1 << 18); i ++){ // 状压int x = 0; // 剩余对象的数量for(int j = 0; j < 18; j ++){if(!(i >> j & 1)) x ++;}if(x & 1) continue ; // 肯定为偶数cnt += x * (x + 1) / 2	;}cout << cnt;return 0;
}

我自己代码经过大量预处理,使得 O ( k ) O(k) O(k) 的复杂度降低为当前剩余对象的数量,得以通过此题。具体见代码和注释。

#include <bits/stdc++.h>
using namespace std;typedef vector<int> Vec;
typedef pair<int, int> pii;const int N = (1 << 19), M = 20;int t, n, m, k;
pii id[M]; // 编号为 i 的对象的坐标
int cr[M][M], val[M], v[M][M]; // cr:坐标为i, j的对象的编号, val:价值, v:编号为i和j的对象一起删除的价值
string s[M][M]; // 对象int get_id(int x, int y){return cr[x][y];
}int f[N]; // 删除状态表示的对象最大价值
// int cmax(int& a, int b){ a = max(a, b); }bool mp[M][M][M][2]; // 从 i 号到 j 号的两条路径上是否存在 k 号void checkl(int id1, int id2){auto [x1, y1] = id[id1]; auto [x2, y2] = id[id2];// 先向下再向左for(int i = x1; i <= x2; i ++){if(s[i][y1][0] != '-') mp[id1][id2][cr[i][y1]][0] = true;}for(int j = y1; j >= y2; j --){if(s[x2][j][0] != '-') mp[id1][id2][cr[x2][j]][0] = true;}// 先向左再向下for(int j = y1; j >= y2; j --){if(s[x1][j][0] != '-') mp[id1][id2][cr[x1][j]][1] = true;}for(int i = x1; i <= x2; i ++){if(s[i][y2][0] != '-') mp[id1][id2][cr[i][y2]][1] = true;}// 将自己编号记为0mp[id1][id2][id1][0] = mp[id1][id2][id2][0] = 0;mp[id1][id2][id1][1] = mp[id1][id2][id2][1] = 0;
}void checkr(int id1, int id2){auto [x1, y1] = id[id1]; auto [x2, y2] = id[id2];// 先下再右for(int i = x1; i <= x2; i ++){if(s[i][y1][0] != '-') mp[id1][id2][cr[i][y1]][0] = true;}for(int j = y1; j <= y2; j ++){if(s[x2][j][0] != '-') mp[id1][id2][cr[x2][j]][0] = true;}// 先右再下for(int j = y1 + 1; j <= y2; j ++){if(s[x1][j][0] != '-') mp[id1][id2][cr[x1][j]][1] = true;}for(int i = x1 + 1; i <= x2; i ++){if(s[i][y2][0] != '-') mp[id1][id2][cr[i][y2]][1] = true;}   // 同理mp[id1][id2][id1][0] = mp[id1][id2][id2][0] = 0;mp[id1][id2][id1][1] = mp[id1][id2][id2][1] = 0;
}void init(){memset(mp, 0, sizeof mp);for(int i = 0; i < t; i ++){for(int j = i + 1; j < t; j ++){int sum = 0;auto [x1, y1] = id[i];auto [x2, y2] = id[j];for(int r = 0; r < k; r ++) sum += (s[x1][y1][r] == s[x2][y2][r]); v[i][j] = val[sum]; // 计算价值if(y2 <= y1) checkl(i, j); // 因为编号大的肯定在下方,但不一定是左边还是右边,分左右分别记录路径else checkr(i, j);}}for(int i = 1; i < (1 << t); i ++) f[i] = -1;
}bool check(int id1, int id2, Vec& vis){int f[] = {1, 1, 1};for(auto x : vis){ // 查询是否两条路径上都有点未删除if(mp[id1][id2][x][0]) f[0] = 0;if(mp[id1][id2][x][1]) f[1] = 0;if(f[0] == 0 && f[1] == 0) return false;}return true;
}void solve(){cin >> n >> m >> k;t = 0;for(int i = 0; i < n; i ++){for(int j = 0; j < m; j ++){cin >> s[i][j];if(s[i][j][0] != '-'){cr[i][j] = t;id[t] = {i, j};t ++; // 给每个非空对象编号}}}for(int i = 0; i <= k; i ++) cin >> val[i];init();for(int i = 0; i < (1 << t); i ++){if(f[i] == -1) continue ;Vec r, g;for(int j = 0; j < t; j ++){if(!(i >> j & 1)) g.push_back(j); // 记录未删除的非空对象}int siz = g.size();for(int j = 0; j < siz; j ++){for(int p = j + 1; p < siz; p ++){ // 枚举两个未删除的对象if(check(g[j], g[p], g)){ // 未删除,check是否能连线f[i | (1 << g[j]) | (1 << g[p])] = max(f[i | (1 << g[j]) | (1 << g[p])], f[i] + v[g[j]][g[p]]); // 转移// cmax(f[i | (1 << j) | (1 << p)], f[i] + d[j][p]); // RE}}}}cout << f[(1 << t) - 1] << "\n";
}int main(){ios::sync_with_stdio(false);cin.tie(0); cout.tie(0);int T;cin >> T;while(T --){solve();}return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电脑时间校对怎么做?看这里,分享4个方法!

“我的电脑时间总是和手机的时间不一样&#xff0c;应该是电脑的时间不准确了&#xff0c;想知道大家遇到这种情况时是如何校对电脑时间的呀&#xff1f;” 随着电脑在我们日常生活中的广泛应用&#xff0c;确保电脑时间准确性变得至关重要。电脑时间校对不仅有助于同步文件和通…

小程序如何设置自动预约快递

小程序通过设置自动预约功能&#xff0c;可以实现自动将订单信息发送给快递公司&#xff0c;快递公司可以自动上门取件。下面具体介绍如何设置。 在小程序管理员后台->配送设置处&#xff0c;选择首选配送公司。为了能够支持自动预约快递&#xff0c;请选择正常的快递公司&…

S4.2.4.5 Fast Training Sequence (FTS)

一 本章节主讲知识点 1.1 FTS的用途和实现注意 二 本章节原文翻译 Fast Training Sequence (FTS) 主要用于在L0s->L0跳转的过程中&#xff0c;让Receiver 检测到电气空闲退出&#xff0c;以及实现bit 和 symbol lock。 2.1 Gen1 and Gen2 速率 对于Gen1/2 FTS的组成如下…

自定义element-ui plus 函数式调用,在API,js中直接使用全局组件

npm方式: npm install -D unplugin-vue-components unplugin-auto-import yarn 方式 : yarn add unplugin-vue-components; yarn add unplugin-auto-import; 使用官方的这个&#xff1a; vite.config.js中配置 plugins: [vue(),AutoImport({resolvers: [ElementPlusResolve…

docker安装(超详细)

一.引言 本安装教程参考Docker官方文档&#xff0c;地址如下&#xff1a;https://docs.docker.com/engine/install/centos/ 二.卸载旧版docker(第一次安装可忽略) 首先如果系统中已经存在旧的Docker&#xff0c;则先卸载&#xff1a; yum remove docker \docker-client \docker…

【漏洞复现】Django -SQL注入漏洞复现(CVE-2019-14234)

感谢互联网提供分享知识与智慧&#xff0c;在法治的社会里&#xff0c;请遵守有关法律法规 复现环境&#xff1a;Vulhub 环境启动后&#xff0c;访问http://192.168.80.141:8000即可看到Django默认首页 漏洞复现 首先登陆后台http://192.168.80.141:8000/admin/&#xff0c;用…

极致性能优化:前端SSR渲染利器Qwik.js | 京东云技术团队

引言 前端性能已成为网站和应用成功的关键要素之一。用户期望快速加载的页面和流畅的交互&#xff0c;而前端框架的选择对于实现这些目标至关重要。然而&#xff0c;传统的前端框架在某些情况下可能面临性能挑战且存在技术壁垒。 在这个充满挑战的背景下&#xff0c;我们引入…

R语言 复习 习题图片

这是日天土申哥不知道从哪淘来的R语言复习知识点图片&#xff0c;大部分内容都是课后习题的答案 加油吧&#xff0c;骚年&#xff0c;考个好分数

注册电气工程师证书挂靠有风险吗?考试难度很高是吗?前景怎么样

一.先说证书挂靠问题&#xff0c;毫无疑问&#xff0c;有风险&#xff0c;远的不说咱说说近的&#xff0c; 比如下面这张图 从上面这张图可以看出来&#xff0c;哪怕是2023&#xff0c;还有陆陆续续的人因为挂靠的问题被处罚&#xff0c;所以我的建议是能不挂就不挂&#xff0c…

吴恩达《机器学习》5-6:向量化

在深度学习和数值计算中&#xff0c;效率和性能是至关重要的。一个有效的方法是使用向量化技术&#xff0c;它可以显著提高计算速度&#xff0c;减少代码的复杂性。接下来将介绍向量化的概念以及如何在不同编程语言和工具中应用它&#xff0c;包括 Octave、MATLAB、Python、Num…

MySQL索引优化与查询优化

1. 索引失效案例 MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了访问高效数据的方法&#xff0c;并且加快查询的速度&#xff0c;因此索引对查询的速度有着至关重要的影响。 使用索引可以快速地定位表中的某条记录&#xff0c;从而提高数据库查询的速…

电商API接口文档|1688API接口的接入获取获得1688商品详情it按关键字搜索商品 按图搜索1688商品(拍立淘)

API接口文档 前面讲到的关于接口的请求方式、类型、原理、安全机制等&#xff0c;产品经理能够简单了解一些就可以满足日常工作中的需要。接口文档是产品经理日常工作中经常会使用到的&#xff0c;特别是做开放平台或B端的产品经理&#xff0c;需要经常的看和写接口文档。 那如…

AI:63-基于Xception模型的服装分类

🚀 本文选自专栏:AI领域专栏 从基础到实践,深入了解算法、案例和最新趋势。无论你是初学者还是经验丰富的数据科学家,通过案例和项目实践,掌握核心概念和实用技能。每篇案例都包含代码实例,详细讲解供大家学习。 📌📌📌在这个漫长的过程,中途遇到了不少问题,但是…

微信小程序案例3-1 比较数字

文章目录 一、运行效果二、知识储备&#xff08;一&#xff09;Page()函数&#xff08;二&#xff09;数据绑定&#xff08;三&#xff09;事件绑定&#xff08;四&#xff09;事件对象&#xff08;五&#xff09;this关键字&#xff08;六&#xff09;setData()方法&#xff0…

【排序算法】 快速排序(快排)!图解+实现详解!

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; 算法—排序篇 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言&#x1f324;️快速排序的概念☁️快速排序的由来☁️快速排序的思想☁️快速排序的实…

开源 Wiki 软件 wiki.js

wiki.js简介 最强大、 可扩展的开源Wiki 软件。使用 Wiki.js 美观直观的界面让编写文档成为一种乐趣&#xff01;根据 AGPL-v3 许可证发布。 官方网站&#xff1a;https://js.wiki/ 项目地址&#xff1a;https://github.com/requarks/wiki 主要特性&#xff1a; 随处安装&a…

【Vue.js】Vue3全局配置Axios并解决跨域请求问题

系列文章目录 文章目录 系列文章目录背景一、部署Axios1. npm 安装 axios2. 创建 request.js&#xff0c;创建axios实例3. 在main.js中全局注册axios4. 在页面中使用axios 二、后端解决跨域请求问题方法一 解决单Contoller跨域访问方法二 全局解决跨域问题 背景 对于前后端分离…

Excel·VBA工作表导出为图片

《Excel转图片别再截图啦&#xff01;用这4个方法&#xff0c;高清且无损&#xff01;》&#xff0c;excel转为图片一般方法较为简单&#xff0c;那么能否使用vba将excel转为图片 选中区域导出为图片 zoom设置为2&#xff0c;导出图片较为清晰 Sub 选中区域导出为图片()Dim …

web3 React dapp中编写balance组件从redux取出并展示用户资产

好啊 上文WEB3 在 React搭建的Dapp中通过redux全局获取并存储用户ETH与自定义token与交易所存储数量中 我们拿到了用户的一个本身 和 交易所token数量 并放进了redux中做了一个全局管理 然后 我们继续 先 起来ganache的一个模拟环境 ganache -d然后 我们启动自己的项目 顺手发…

聚观早报 |小米CarWith启动兼容测试;「天工」大模型开放

【聚观365】11月6日消息 小米CarWith启动兼容测试 「天工」大模型开放 邮政快递揽收超20亿件 华为Mate 60 Pro开启预约申购 VERTU持续探索前沿科技 小米CarWith启动兼容测试 小米CarWith是打造“人车家生态”中不可或缺的一环&#xff0c;在最新升级的2.0版本中&#xff…