A. Countdown
题意:
有一个有前导零的数字,每次操作可以使该数字减一,或者交换该数字上任意两个位置的数。
询问最少需要多少次操作可以使得该数变为全0。
分析:
可以发现对于位数越低的位,减一操作越少,所以我们可以贪心的进行减一操作,即将其他所有位置上的数字都交换到个位上进行减一,所以答案就是所有位上的数字加和,加上除最后一位以外非0位的个数(交换次数)。
ACcode:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 3e5 + 100;
const int mod = 998244353;
void solve() {
int n;
ll res = 0;
scanf("%d", &n);
for(int i = 1; i <= n; ++ i) {
int x;
scanf("%1d", &x);
if(x != 0 && i != n) res ++;
res += x;
}
printf("%lld\n", res);
}
int main() {
int t;
scanf("%d", &t);
while(t --)
solve();
return 0;
}
/*
*/
B. Swaps
题意:
有一个包含从
[
1
,
2
n
]
[1,2n]
[1,2n]内的所有奇数的无序数组A,还有一个包含从
[
1
,
2
n
]
[1,2n]
[1,2n]内的所有偶数的无序数组B。
你可以进行一种操作:首先选中两个数组中的任意一个数组,然后选择此数组两个相邻的位置,将两个位置的值交换。
询问最少换多少次可以使得A数组的字典序小于B数组。
分析:
首先如果想要使得A的字典序小于
B
B
B,那么就只需要
A
1
<
B
1
A_1 < B_1
A1<B1。
我们可以找到
A
A
A数组中的一个数
A
i
A_i
Ai,
B
B
B数组中的一个数
B
j
B_j
Bj,满足
A
i
<
B
j
A_i < B_j
Ai<Bj,然后将
A
i
A_i
Ai和
B
j
B_j
Bj同时通过操作移动到数组的第一位,那么答案就是
i
+
j
−
2
i + j - 2
i+j−2。
因为
A
A
A,
B
B
B数组里的数都是固定的,所以我们可以通过
O
(
2
∗
n
)
O(2*n)
O(2∗n)的复杂度枚举每一个数字,处理出来 大于当前奇数的所有的偶数 移动到
B
B
B数组的第一位所需要的操作数的最小值
x
x
x,设
y
y
y为当前奇数移动到
A
A
A数组的第一位所需要的操作数,
x
+
y
x+y
x+y即为将当前奇数移动到
A
A
A数组的第一位的所有情况中的最小值。
再将
[
1
,
2
n
]
[1,2n]
[1,2n]所有的奇数都做此操作,对所有的
x
+
y
x+y
x+y求一个最小值,即为答案。
ACcode:
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
const int N = 3e5 + 100;
const int mod = 998244353;
const int inf = 0x3f3f3f3f;
int mp[N]; //mp[i]表示将i这个数字移动到数组首位需要的操作数
void solve() {
int n;
scanf("%d", &n);
for(int i = 0; i < n; ++ i) {
int x;
scanf("%d", &x);
mp[x] = i;
}
for(int i = 0; i < n; ++ i) {
int x;
scanf("%d", &x);
mp[x] = i;
}
int ans = inf, res = inf;
for(int i = 2 * n; i >= 1; -- i) {
if(i & 1) ans = min(ans, mp[i] + res);
else res = min(res, mp[i]);
}
printf("%d\n", ans);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int t;
scanf("%d", &t);
while(t --)
solve();
return 0;
}
/*
5
9 3 7 1 5
2 8 6 4 10
*/
C. Book
题意:
有一本书,里面有n个章节。为了去理解某些章节,需要先把此章节的所有前置章节理解了。
刚开始的时候你不理解任何一个章节,你会从头到尾阅读这本书,遇到可以理解的章节就会阅读并理解,求需要阅读这本书多少次可以将全部章节理解。
分析:
很明显就是一个拓扑排序问题,这里有两种做法:
第一种:
使用优先队列进行拓扑排序,保证先处理轮数小的章节。
对于一个入度为0的点y来说,如果将y到x的边删除之后,使得x的入度为0,那么就比较x和y编号的大小关系,如果y < x的话,说明x这个章节也可以在这一轮内被理解;如果x < y的话,说明x这个章节需要在下一轮中才能被理解。
最终读书的轮数+1就是答案,如果原图不是个拓扑图答则案为-1。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
typedef pair<int, int> PII;
int n;
int d[N];
void solve() {
priority_queue<PII, vector<PII>, greater<PII> > Q;
scanf("%d", &n);
vector<int> G[n + 1];
for(int i = 1; i <= n; ++ i) d[i] = 0;
for(int i = 1; i <= n; ++ i) {
int k;
scanf("%d", &k);
while(k --) {
int p;
scanf("%d", &p);
G[p].push_back(i);
d[i] ++;
}
}
int res = 0, ans;
for(int i = 1; i <= n; ++ i) if(!d[i]) Q.push(make_pair(1, i));
while(Q.size()) {
auto head = Q.top();
Q.pop();
res ++, ans = head.first;
for(int j = 0; j < G[head.second].size(); ++ j) {
int to = G[head.second][j];
d[to] --;
if(!d[to]) {
if(to < head.second) Q.push(make_pair(head.first + 1, to));
else Q.push(make_pair(head.first, to));
}
}
}
if(res == n) printf("%d\n", ans);
else printf("-1\n");
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int t;
scanf("%d", &t);
while(t --) {
solve();
}
return 0;
}
第二种:
直接建图,若a→b,且a < b的话,则边权为0,否则边权为1(边权为0代表能在当前轮理解,边权为1表示需要在下一轮理解)。
我们只需要对这个图求一个最长路径,这个最长路径+1就是答案(最长路径表示需要经过这么多轮才能到达最后的那个点)。
最长路按拓扑序dp一下就可以求出来。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 100;
int h[N], ne[N], e[N], w[N], idx;
int dp[N], d[N];
vector<int> sorted;
int n;
void init() {
memset(h, -1, sizeof h);
for(int i = 1; i <= n; ++ i) dp[i] = d[i] = 0;
idx = 0;
}
void add(int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
bool topsort() {
queue<int> Q;
sorted.clear();
for(int i = 1; i <= n; ++ i) if(!d[i]) Q.push(i);
while(Q.size()) {
int head = Q.front();
Q.pop();
sorted.push_back(head);
for(int i = h[head]; ~i; i = ne[i]) {
int to = e[i];
d[to] --;
if(!d[to]) Q.push(to);
}
}
return sorted.size() == n;
}
void solve() {
scanf("%d", &n);
init();
for(int i = 1; i <= n; ++ i) {
int k;
scanf("%d", &k);
while(k --) {
int p;
scanf("%d", &p);
add(p, i, (p < i ? 0 : 1));
d[i] ++;
}
}
int res = topsort();
if(!res) {
printf("-1\n");
return ;
}
for(int i = 0; i < sorted.size(); ++ i) {
int v = sorted[i];
for(int j = h[v]; ~j; j = ne[j]) {
int to = e[j];
if(dp[to] < dp[v] + w[j])
dp[to] = dp[v] + w[j];
}
}
int ans = 0;
for(int i = 1; i <= n; ++ i) ans = max(ans, dp[i]);
printf("%d\n", ans + 1);
}
int main() {
#ifndef ONLINE_JUDGE
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
#endif
int t;
scanf("%d", &t);
while(t --) {
solve();
}
return 0;
}