上海计算机学会6月乙组比赛题解
本次比赛考了一道STL、两道贪心、一道动态规划。题目难度低于CSP-J,题目简短易懂,思维难度不大,贪心的套路还是比较常见的,第四题动态规划的状态转移方程还是比较好想的。
比赛链接:https://iai.sh.cn/contest/52
第一题:T1两数归零
标签:
S
T
L
、
m
a
p
STL、map
STL、map
题意:给定
n
n
n个整数
a
1
,
a
2
,
a
3
,
.
.
.
,
a
n
a_1,a_2,a_3,...,a_n
a1,a2,a3,...,an,请统计有多少二元组
i
,
j
i,\ j
i, j满足
i
<
j
i < j
i<j 且满足
a
i
+
a
j
=
0
a_i+a_j=0
ai+aj=0。
数据范围:
1
<
=
n
<
=
300000
1<=n<= 300000
1<=n<=300000,
−
1
,
000
,
000
,
000
≤
a
i
≤
1
,
000
,
000
,
000
−1,000,000,000≤a_i≤1,000,000,000
−1,000,000,000≤ai≤1,000,000,000
题解:直接从前往后遍历一遍,到第
i
i
i个数
a
i
a_i
ai的时候,看看前面出现过的相反数
−
a
i
-a_i
−ai有多少个,把计数加上去,最后就是答案了。该题
a
i
a_i
ai数据范围比较大,并且有负数,所以我们需要用
m
a
p
map
map维护一下。
代码:
#include <bits/stdc++.h>
using namespace std;
int a[300005];
map<int, int> m;
int main() {
int n, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
ans += m[-a[i]];
m[a[i]]++;
}
cout << ans << endl;
return 0;
}
第二题:T2牛奶供应(四)
标签:结构体排序、贪心算法
题意:给定
n
n
n次能够购买原料的机会,第
i
i
i次在第
d
i
d_i
di天,那天能够提供原料
w
i
w_i
wi升,每升价格是
p
i
p_i
pi元。
并且每升牛奶每天的存储费用是
1
1
1元,求需要在第
m
m
m天后提供
L
L
L升加工后的牛奶,如何去收购并加工存储的总费用最少。
数据范围:
1
<
=
n
<
=
1
0
5
1<=n<=10^5
1<=n<=105,
1
<
=
L
<
=
1
0
6
1<=L<=10^6
1<=L<=106,
1
<
=
m
,
w
i
,
p
i
<
=
1
0
6
1<=m,w_i,p_i<=10^6
1<=m,wi,pi<=106,$1<= d_i <m $
题解:首先,看到数据范围这么大,小估估一下就得开
l
o
n
g
l
o
n
g
long\ long
long long,先全局开了。然后考虑
p
i
p_i
pi是第
i
i
i次购买的单价,那我们换个思路一想,就能得到把第
i
i
i次每升牛奶存到第
m
m
m天的费用就是:
p
i
+
m
−
d
i
p_i+m-d_i
pi+m−di(该次每升单价+每升从第
d
i
d_i
di天到第
m
m
m天存储费用)。我们根据费用这个进行结构体排序一下就行了,再从便宜的开始拿直到拿到
L
L
L升算一下总费用就好了。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
ll d, w, p;
}a[100005];
bool cmp(node x, node y) {
return x.p < y.p;
}
int main() {
ll n, m, L;
cin >> n >> m >> L;
for (int i = 1; i <= n; i++) {
cin >> a[i].d >> a[i].w >> a[i].p;
a[i].p = a[i].p + m - a[i].d; // 每升原料到第m天需要的费用
}
sort(a + 1, a + 1 + n, cmp);
ll ans = 0;
for (int i = 1; i <= n; i++) {
if (L >= a[i].w) {
ans += a[i].w * a[i].p;
L -= a[i].w;
} else {
ans += L * a[i].p;
L = 0;
}
}
cout << ans << endl;
return 0;
}
第三题:T3工作安排
标签:结构体排序、贪心算法
题意:有
n
n
n份任务。完成第
i
i
i份任务需要
t
i
t_i
ti的时间,在这份任务没有完成之前,每一个单位时间会收到
f
i
f_i
fi单位的罚款。求以什么顺序安排这些任务,才能使罚款总额最少?
数据范围:
1
<
=
n
,
t
i
,
f
i
<
=
200
,
000
1<=n,\ t_i,\ f_i<=200,000
1<=n, ti, fi<=200,000
题解:这道题有点像 洛谷P1012 [NOIP1998 提高组] 拼数 这道题。
本质来说就是进行结构体排序的时候,比较的内容不再是简单的两个对象(比如代码里面
x
x
x和
y
y
y)各自的数据内容,而是说需要两者结合进行比较。
像这道题,有两份任务
x
x
x和
y
y
y,我到底是把
x
x
x先完成还是先把
y
y
y先完成,我们需要考虑到先完成
x
x
x任务,那对于
y
y
y来说就得等
x
.
t
x.t
x.t的时间,那罚款就是
x
.
t
∗
y
.
f
x.t*y.f
x.t∗y.f;同理先完成
y
y
y任务对于
x
x
x来说的罚款是
y
.
t
∗
x
.
f
y.t*x.f
y.t∗x.f,我们需要贪心的考虑哪种所带来的罚款会少点。
还有个细节就是等待时间应该是累积的,对于第
i
i
i项任务没完成之前,需要收到罚款的时间是前
i
i
i项任务时间之和。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
ll t, f;
}p[200005];
bool cmp(node x, node y) {
return x.t * y.f < y.t * x.f;
}
int main() {
ll n, sum = 0, ans = 0;
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> p[i].t >> p[i].f;
}
sort(p + 1, p + 1 + n, cmp);
for (int i = 1; i <= n; i++) {
sum += p[i].t;
ans += p[i].f * sum;
}
cout << ans << endl;
return 0;
}
第四题:T4单词解密
标签:动态规划
题意:有套加密系统,本质是用字母在字典序中的编号来代替该字母。比如单词
i
a
i
iai
iai,
i
i
i是第
9
9
9个字母,
a
a
a是第
1
1
1个字母,因此
i
a
i
iai
iai加密之后得到的密文是
919
919
919。但是
919
919
919除了
i
a
i
iai
iai,还可能解读成
i
s
is
is,
s
s
s是第
19
19
19个字母。现在给定一串密文,求出所有原文的可能性种数
%
1000000007
\%1000000007
%1000000007。
数据范围:给定密文长度不超过
1
0
5
10^5
105位
题解:首先设置
d
p
[
i
]
dp[i]
dp[i]:前
i
i
i个字符的种类,因为最多
26
26
26个字母,那么有两种情况:
- 考虑状态转移方程从 d p [ i − 1 ] dp[i-1] dp[i−1]转移过来,即第 i i i个数字单独代表一个字母
- 考虑状态转移方程从 d p [ i − 2 ] dp[i-2] dp[i−2]转移过来,即第 i i i个数字和前一个第 i − 1 i-1 i−1个数字凑一下代表一个字母
第二步转移的时候需要去考虑凑起来的数字范围必须在
10
−
26
10-26
10−26,如何小于
10
10
10会重复计算,如果大于
26
26
26是不合法的情况,这两种情况都不应该进行转移。
下面代码,因为
s
t
r
i
n
g
string
string字符串是从
0
0
0开始存的,为了方便起见,
d
p
dp
dp答案记录我都往后推了一位,注意理解。
代码:
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000000007;
int dp[100005]; // dp[i]:前i个字符的种类
int main() {
string s;
cin >> s;
int n = s.size();
dp[0] = 1;
for (int i = 0; i < n; i++) {
if (s[i] >= '1' && s[i] <= '9') {
dp[i+1] = (dp[i+1] + dp[i]) % mod;
}
if (i >= 1) {
int num = 10 * (s[i - 1] - '0') + (s[i] - '0');
if (num >= 10 && num <= 26) dp[i+1] = (dp[i+1] + dp[i-1]) % mod;
}
}
cout << dp[n] << endl;
return 0;
}