ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

2022“杭电杯”中国大学生算法设计超级联赛(3)

2022-08-03 22:00:40  阅读:178  来源: 互联网

标签:联赛 point int max LL long 2022 杭电杯 mx


比赛链接:

https://vjudge.net/contest/507736

B - Boss Rush

题意:

有 \(n\) 个技能,第 \(i\) 个技能使用完后的 \(t_i\) 时间内不能使用其他技能,该技能会在 \(len_i\) 的时间中,每秒造成 \(d[i][j]\) 点伤害 \((1 <= j <= len_i)\),boss 有 \(H\) 滴血,问最短多少时间能杀死 boss。

思路:

先二分时间,然后通过记忆化搜索 + 状态压缩判断指定时间内是否能造成相对应的伤害。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int N = 20, M = 1e5 + 10, K = 3e5 + 10;
LL n, H, t[N], len[N], d[N][M], pre[N][M], dp[K];
bool check(LL x){
	for (int i = 0; i < (1 << n); i ++ ){
		dp[i] = -1;
	}
	function<LL(LL)> dfs = [&](LL state){
		if ( ~ dp[state]) return dp[state];
		LL ans = 0, sumt = 0;
		for (int i = 0; i < n; i ++ ){
			if (state >> i & 1){
				sumt += t[i + 1];  //已经使用的技能的冷却时间
			}
		}
		if (sumt > x) return dp[state] = 0;
		for (int i = 0; i < n; i ++ ){
			if ( ! (state >> i & 1)){  //使用新的技能
				ans = max(ans, pre[i + 1][min(len[i + 1], x - sumt)] + dfs(state | (1 << i)));
			}
		}
		return dp[state] = ans;
	};
	return dfs(0) >= H;
}
void solve(){
	cin >> n >> H;
	LL mx = 0;
	for (int i = 1; i <= n; i ++ ){
		cin >> t[i] >> len[i];
		mx += t[i] + len[i];
		for (int j = 1; j <= len[i]; j ++ )
			cin >> d[i][j];
		for (int j = 1; j <= len[i]; j ++ )
			pre[i][j] = pre[i][j - 1] + d[i][j];
	}
	LL L = 0, R = mx + 1;
	while(L < R){
		LL mid = (L + R) >> 1;
		if (check(mid)){
			R = mid;
		}
		else{
			L = mid + 1;
		}
	}
	if (L > mx) cout << "-1\n";
	else cout << L - 1 << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

C - Cyber Language

题意:

给定一行句子,输出每个单词的首字母的大写。

思路:

签到,按题意操作。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
char s[100];
void solve(){
	gets(s);
	for (int i = 0; i < strlen(s); i ++ )
		if (i == 0 || s[i - 1] == ' ')
			cout << (char)(s[i] - 'a' + 'A');
	cout << "\n";
}
int main(){
	LL T = 1;
	cin >> T;
	getchar();  //把换行读取了
	while(T -- ){
		solve();
	}
	return 0;
}

I - Package Delivery

题意:

快递站有 \(n\) 个包裹,每个包裹从第 \(l\) 天存放到第 \(r\) 天,每次去快递站可以拿 \(k\) 个包裹,问最少去快递站几次能拿到所有包裹。

思路:

对于第 \(i\) 个快递,它的时间为 \(l_i\) 到 \(r_i\)。
按照贪心的策略,肯定拿 \(r_i\) 最小的那个快递,再从所有 \(l_j <= r_i <= r_j\) 的快递中选 \(r_j\) 最小的 \(k\) 个拿走。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL n, k;
	cin >> n >> k;
	vector < pair<LL, pair<LL, LL> > > st(n);
	vector < pair<LL, LL> > ed(n);
	for (int i = 0; i < n; i ++ ){
		cin >> st[i].first >> ed[i].first;
		st[i].second = {ed[i].first, i};
		ed[i].second = i;
	}
	sort(st.begin(), st.end());
	sort(ed.begin(), ed.end());
	priority_queue < pair<LL, LL>, vector< pair<LL, LL> >, greater< pair<LL, LL> > > q;
	vector <bool> used(n, false);
	LL ans = 0;
	for (int i = 0, j = 0; i < n; i ++ ){
		if (used[ed[i].second]) continue;
		while(j < n && st[j].first <= ed[i].first){
			q.push(st[j].second);
			j ++ ;
		}
		ans ++ ;
		for (int t = 0; t < k; t ++ ){
			if (q.empty()) break;
			used[q.top().second] = true;
			q.pop();
		}
	}
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

K - Taxi

题意:

有 \(n\) 个小镇,第 \(i\) 个小镇的位置为 \((x_i,y_i)\),费用为 \(w_i\),对于一个点 \((x^{'}, y^{'})\),它到第 \(i\) 个小镇的费用为 \(min(w_i, \lvert x^{'} - x_i \rvert + \lvert y^{'} - y_i \rvert)\),现有 q 次询问,每次告诉一个点 \((x^{'}, y^{'})\),问小镇到这个点的费用最大为多少?。

思路:

\(max(\lvert x^{'} - x_i \rvert, \lvert y^{'} - y_i \rvert)\)
= \(max(max(x^{'} - x_i, - x^{'} + x_i), max(y^{'} - y_i, - y^{'} + y_i))\)
= max{\((x^{'} + y^{'}) + (- x_i - y_i), (x^{'} - y^{'}) + (- x_i + y_i), (- x^{'} + y^{'}) + (x_i - y_i), (- x^{'} - y^{'}) + (x_i + y_i)\)}
所以只需要预处理出所有城镇坐标的四个属性,就可以通过 O(1) 的方法计算曼哈顿距离的答案。
而 \(w\) 则没办法,所以对 \(w\) 进行排序,使其单调,那么就可以通过二分取选择 \(w\)。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL N = 1e5 + 10, INF = 1e18;
struct node{
	LL x, y, w;
	bool operator < (const node & x) const{
		return w < x.w;
	}
}point[N];
LL a[N], b[N], c[N], d[N], ans;
void solve(){
	LL n, q;
	cin >> n >> q;
	for (int i = 1; i <= n; i ++ )
		cin >> point[i].x >> point[i].y >> point[i].w;
	sort(point + 1, point + n + 1);
	a[n + 1] = b[n + 1] = c[n + 1] = d[n + 1] = -INF;
	for (int i = n; i >= 1; i -- ){
		a[i] = max(a[i + 1], point[i].x + point[i].y);
		b[i] = max(b[i + 1], point[i].x - point[i].y);
		c[i] = max(c[i + 1], - point[i].x + point[i].y);
		d[i] = max(d[i + 1], - point[i].x - point[i].y);
	}
	while(q -- ){
		LL x, y;
		cin >> x >> y;
		ans = 0;
		LL L = 1, R = n;
		function <bool(LL)> check = [&](LL k){
			LL mx = a[k] - x - y;
			mx = max(mx, b[k] - x + y);
			mx = max(mx, c[k] + x - y);
			mx = max(mx,  d[k] + x + y);
			ans = max(ans, min(point[k].w, mx));
			return point[k].w < mx;
		};
		while(L < R){
			LL mid = (L + R) >> 1;
			if (check(mid)){
				L = mid + 1;
			}
			else{
				R = mid;
			}
		}
		check(L);
		cout << ans << "\n";
	}
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

L - Two Permutations

题意:

给定两个序列 \(p\) 和 \(q\),每次重构从其中一个序列中取出首位元素,然后加入序列 \(r\) 的后面,直到两个序列所有元素被取完结束,现在给定序列 \(s\),问有多少种取法可以让 \(r = s\)。

思路:

因为每次只从 \(p\) 或者 \(q\) 中取出元素,所以可以定义 \(dp[i][0]\) 表示 \(r\) 序列的第 \(i\) 位是从 \(p\) 序列中取出来的方案数,\(dp[i][1]\) 表示 \(r\) 序列的第 \(i\) 位是从 \(q\) 序列中取出来的方案数。
如果该元素本来就是从本序列中取出,即 \(s[i - 1]\) 和 \(s[i]\) 是从同一个序列中取出的,那么它们位置的坐标差为 1。
如果是从不同序列中取出的,因为之前总共取了 \(i\) 个元素,那么两个序列中的位置之和一定等于 \(i\)。
所以在动态规划之前,先记录两个序列中每个元素的位置即可。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const int mod = 998244353;
void solve(){
	LL n;
	cin >> n;
	vector <LL> p(n + 1), q(n + 1), id1(n + 1), id2(n + 1), s(2 * n + 1);
	for (int i = 1; i <= n; i ++ ){
		cin >> p[i];
		id1[p[i]] = i;
	}
	for (int i = 1; i <= n; i ++ ){
		cin >> q[i];
		id2[q[i]] = i;
	}
	for (int i = 1; i <= 2 * n; i ++ )
		cin >> s[i];
	vector < vector<LL> > dp(2 * n + 1, vector<LL>(2));
	dp[0][0] = 1;
	LL ans = 0;
	for (int i = 1; i <= 2 * n; i ++ ){
		LL pre = s[i - 1], now = s[i];
		if (id1[now] == id1[pre] + 1) dp[i][0] = (dp[i][0] + dp[i - 1][0]) % mod;
		if (id1[now] + id2[pre] == i) dp[i][0] = (dp[i][0] + dp[i - 1][1]) % mod;
		if (id2[now] == id2[pre] + 1) dp[i][1] = (dp[i][1] + dp[i - 1][1]) % mod;
		if (id2[now] + id1[pre] == i) dp[i][1] = (dp[i][1] + dp[i - 1][0]) % mod;
	}
	cout << (dp[2 * n][0] + dp[2 * n][1]) % mod << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T = 1;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

标签:联赛,point,int,max,LL,long,2022,杭电杯,mx
来源: https://www.cnblogs.com/Hamine/p/16535981.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有