蓝桥2022决赛B题解
T1 卡牌
题目描述
这天,小明在整理他的卡牌。
他一共有 n 种卡牌,第 i 种卡牌上印有正整数数 i(i ∈ [1, n]),且第 i 种卡牌 现有 ai 张。
而如果有 n 张卡牌,其中每种卡牌各一张,那么这 n 张卡牌可以被称为一 套牌。小明为了凑出尽可能多套牌,拿出了 m 张空白牌,他可以在上面写上数 i,将其当做第 i 种牌来凑出套牌。然而小明觉得手写的牌不太美观,决定第 i 种牌最多手写 bi 张。
请问小明最多能凑出多少套牌?
输入格式
输入共 3 行,第一行为两个正整数 n, m。
第二行为 n 个正整数 a1, a2, …, an。
第三行为 n 个正整数 b1, b2, …, bn。
输出格式
一行,一个整数表示答案。
数据范围
对于 30% 的数据,保证 n ≤ 2000 ;
对于 100% 的数据,保证 n ≤ 2 × 105 ; ai , bi ≤ 2n; m ≤ n2 。
思路
先以a的大小做一遍sort从小到大排序,想要直接找到答案的写法相对比较麻烦,可以直接二分答案做判定,判定一个答案的可行性比直接找到答案简单。
贴ACcode
#include
#include
#include
#define Maxn 200001
using namespace std;
long long n,m;
struct Card{
int a,b;
int operator < (const Card &x) const{
return a > x.a;
}
}card[Maxn];
int check(int x){
long long num = 0;
for(int i = 1;i <= n;i ++){
int def = x - card[i].a;
if(def > card[i].b) return 0;
else {
num += max(def,0);
if(num > m) return 0;
}
}
return 1;
}
int main(){
cin >> n >> m;
for(int i = 1;i <= n;i ++) cin >> card[i].a;
for(int i = 1;i <= n;i ++) cin >> card[i].b;
int l = 1,r = n;
while(r - l >= 0){
int mid = (l + r) >> 1;
if(check(mid)) l = mid + 1;
else r = mid - 1;
}
if(check®) cout << r;
else cout << l;
}
T2 最大数字
题目描述
给定一个正整数 N。你可以对 N 的任意一位数字执行任意次以下 2 种操作:
将该位数字加 1。如果该位数字已经是 9,加 1 之后变成 0。
将该位数字减 1。如果该位数字已经是 0,减 1 之后变成 9。
你现在总共可以执行 1 号操作不超过 A 次,2 号操作不超过 B 次。
请问你最大可以将 N 变成多少?
输入格式
第一行包含 3 个整数:N, A, B。
输出格式
一个整数代表答案。
数据范围
对于 30% 的数据,1 ≤ N ≤ 100; 0 ≤ A, B ≤ 10
对于 100% 的数据,1 ≤ N ≤ 10^17; 0 ≤ A, B ≤ 100
思路
先考虑对一位数字的操作,大位数字的影响绝对优先于小位,可以有一个贪心:优先使大位的数字达到9或尽可能使最高位更高,但是具体的操作顺序,不能直接判断出用操作1还是2更优,考虑Dfs,从大往小搜。
#include
#include
#include
using namespace std;
long long n,ans = 0;
int a,b,x[21],cnt = 0;
void Dfs(int now,int a,int b,int t){
if(x[now] == 9 && now <= cnt) Dfs(now + 1,a,b,x[now + 1]);
if(now > cnt) {
long long sum = 0;
for(int i = 1;i <= cnt;i ++){
sum *= 10;
sum += x[i];
}
ans = max(ans,sum);
return;
}
if(9 - t <= a) {
x[now] = 9;
Dfs(now + 1,a - (9 - t),b,x[now + 1]);
x[now] = t;
}
else{
x[now] = t + a;
Dfs(now + 1,0,b,x[now + 1]);
x[now] = t;
}
if(x[now] + 1 <= b) {
x[now] = 9;
Dfs(now + 1,a,b - (t + 1),x[now + 1]);
x[now] = t;
}
}
int main(){
cin >> n >> a >> b;
long long u = n;
while(n){cnt ++,n /= 10;}
for(int i = cnt;i >= 1;i --){
x[i] = u % 10;
u /= 10;
}
Dfs(1,a,b,x[1]);
cout << ans;
}
T3 出差
题目描述
A 国有 N 个城市,编号为 1 . . . N。小明是编号为 1 的城市中一家公司的员工,今天突然接到了上级通知需要去编号为 N 的城市出差。
由于疫情原因,很多直达的交通方式暂时关闭,小明无法乘坐飞机直接从城市 1 到达城市 N,需要通过其他城市进行陆路交通中转。小明通过交通信息网,查询到了 M 条城市之间仍然还开通的路线信息以及每一条路线需要花费的时间。
同样由于疫情原因,小明到达一个城市后需要隔离观察一段时间才能离开该城市前往其他城市。通过网络,小明也查询到了各个城市的隔离信息。(由于小明之前在城市 1,因此可以直接离开城市 1,不需要隔离)
由于上级要求,小明希望能够尽快赶到城市 N,因此他求助于你,希望你能帮他规划一条路线,能够在最短时间内到达城市 N。
输入格式
第 1 行:两个正整数 N, M, N 表示 A 国的城市数量,M 表示未关闭的路线数量
第 2 行:N 个正整数,第 i 个整数 Ci 表示到达编号为 i 的城市后需要隔离的时间
第 3 . . . M + 2 行:每行 3 个正整数,u, v, c,表示有一条城市 u 到城市 v 的双向路线仍然开通着,通过该路线的时间为 c
输出格式
第 1 行:1 个正整数,表示小明从城市 1 出发到达城市 N 的最短时间(到达城市 N,不需要计算城市 N 的隔离时间)
数据范围
对于 100% 的数据,1 ≤ N ≤ 1000 , 1 ≤ M ≤ 10000, 1 ≤ Ci ≤ 200, 1 ≤ u, v ≤ N, 1 ≤ c ≤ 1000
思路
最短路板子题,把隔离时间视为点权加到边权上即可。
贴ACcode:
#include
#include
#include
#include
#include
#include
using namespace std;
struct Edge{
int to,val;
}edge;
struct Node{
int index,dis;
bool operator < (const Node &x) const{
return dis > x.dis;
}
}node;
priority_queue q;
vector Point[10001];
long long dis[10001];
int c[10001],vis[10001];
void Dij(int start){
dis[start] = 0;
node.index = start;
node.dis = 0;
q.push(node);
while(!q.empty()){
node = q.top();
q.pop();
int u = node.index,d = node.dis;
// printf(“%d %d\n”,u,d);
if(vis[u]) continue;
vis[u] = 1;
for(int i = 0;i < Point[u].size();i ++){
int v = Point[u][i].to,w = Point[u][i].val;
if(dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
if(!vis[v]){
node.index = v;
node.dis = dis[v];
q.push(node);
// printf(“%d %d\n”,v,dis[v]);
}
}
}
}
}
int main(){
int N,M;
cin >> N >> M;
for(int i = 1;i <= N;i ++) cin >> c[i];
for(int i = 1;i <= M;i ++){
int u,v,w;
cin >> u >> v >> w;
edge.to = v;
edge.val = w + c[v];
Point[u].push_back(edge);
edge.to = u;
edge.val = w + c[u];
Point[v].push_back(edge);
}
memset(dis,1000000001,sizeof(dis));
memset(vis,0,sizeof(vis));
Dij(1);
cout << dis[N] - c[N];
}
但是不知道为什么总是98。。。。不想调了