上海计算机学会6月乙组比赛题解

上海计算机学会6月乙组比赛题解

本次比赛考了一道STL、两道贪心、一道动态规划。题目难度低于CSP-J,题目简短易懂,思维难度不大,贪心的套路还是比较常见的,第四题动态规划的状态转移方程还是比较好想的。

比赛链接:https://iai.sh.cn/contest/52

第一题:T1两数归零

标签 S T L 、 m a p STL、map STLmap

题意:给定 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,000ai1,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+mdi(该次每升单价+每升从第 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.ty.f;同理先完成 y y y任务对于 x x x来说的罚款是 y . t ∗ x . f y.t*x.f y.tx.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个字母,那么有两种情况:

  1. 考虑状态转移方程从 d p [ i − 1 ] dp[i-1] dp[i1]转移过来,即第 i i i个数字单独代表一个字母
  2. 考虑状态转移方程从 d p [ i − 2 ] dp[i-2] dp[i2]转移过来,即第 i i i个数字和前一个第 i − 1 i-1 i1个数字凑一下代表一个字母

第二步转移的时候需要去考虑凑起来的数字范围必须在 10 − 26 10-26 1026,如何小于 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;
}
  • 14
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值