Codeforces 938

文章介绍了五道来自Codeforces竞赛的编程题目,涉及酸奶销售策略、矩阵构建、击沉船只计数、子序列搜索和字符反转问题,展示了如何通过算法优化求解这些问题的最优解。
摘要由CSDN通过智能技术生成

Dashboard - Codeforces Round 938 (Div. 3) - Codeforces

A.Yogurt Sale

题目大意:一共要买 n 盒酸奶,有两种方案购买

                方案 1:买一盒花 a 块钱

                方案 2:两盒花 b 块钱

                问买 n 盒酸奶最少花多少钱

首先判断如果用方案 1 买两盒花费2a,如果2a比b大,则不需要进行额外的判断,全部用方案1购买即可

如果2a比b小,那如果购买数量>2,那就优先选择方案2,然后就可以推到出用方案2购买n-n%2个酸奶,即花费b*n/2,用方案一购买n%2个,即花费a*(n%2),共花费b*n/2+a*(n%2)

        int n, a, b;
		cin >> n >> a >> b;
		if (a + a <= b) cout << n*a << '\n';
		else cout << (n / 2)*b + (n % 2)*a << '\n';

B. Progressive Square

题目大意:给一个n,和n*n个数,问这些数能否构成行数和列数都为n,且相邻行之间的差为c,相邻列之间的差为d的矩阵

首先我们可以找到这n*n个数中最小的数,最小的这个数一定是左上角的数,然后可以以此为基点,构造出相邻行的差为c,相邻列的差为d的矩阵,然后判断构造出的矩阵中的数是否都能在给出的数中找到相对应的即可

        int n, a, b;
		cin >> n >> a >> b;
		multiset<int> s;
		for (int i = 1; i <= n*n; i++) {
			int t;
			cin >> t;
			s.insert(t);
		}
		multiset<int> s1;
		s1.insert(*s.begin());
		for (int i = 1; i < n; i++) {
			s1.insert((*s1.begin()) + a * i);
		}
		multiset<int> s2;
		for (int i = 1; i < n; i++) {
			for (int j : s1) {
				s2.insert(j + i * b);
			}
		}
		for (int i : s1) s2.insert(i);
		for (int i : s2) {
			if (s.find(i) != s.end()) {
				s.erase(s.find(i));
			}
		}
		if (s.size() == 0) cout << "YES\n";
		else cout << "NO\n";

C. Inhabitant of the Deep Sea

题目大意:共n艘船,k枚弹药,每艘船都有自己的血量,按照 { 1.打最左边  2.打最右边 } 的顺序击打飞船,每次打一枚弹药,船掉一滴血,如果飞船没有血量了,则沉落,不再攻击它,问最多可以打掉多少艘船

分析一下题意,可以知道k枚弹药,从左打的用了k/2+k%2个弹药,从右打的用了k/2个弹药

可以用前缀和的思想,求出以左端为起点的前缀和数组 l 【】和以右端点为其顶啊的前缀和(后缀和?)的数组 r【】,首先特殊判断一下k是否大于 l 【n】,即是否可以把所有的船都击落。

如果不能,那么在 l 数组中找到某个位置 t ,满足 k/2+k%2 >= l【t】 &&  k/2+k%2< l【t+1】,

同样的,在 r 数组中也找到某个位置 p ,满足 k/2>= r【p】 && k/2 < r【p-1】;

答案即为 t+n-p+1

        int n, k;
		cin >> n >> k;
		l[0] = 0, r[n + 1] = 0;
		for (int i = 1; i <= n; i++) {
			cin >> a[i];
			l[i] = l[i - 1] + a[i];
		}
		for (int i = n; i >= 1; i--) {
			r[i] = r[i + 1] + a[i];
		}
		if (k >= l[n]) cout << n << '\n';
		else {
			int ll = (k + 1) / 2, rr = k / 2;
			int num = 0;
			for (int i = 1; i <= n; i++) {
				if (ll >= l[i] && ll < l[i + 1]) {
					num = i;
 
					break;
				}
			}
			for (int i = n; i >= 1; i--) {
				if (rr >= r[i] && rr < r[i - 1]) {
					num += (n - i + 1);
					break;
				}
			}
			cout << num << '\n';
		}

D. Inaccurate Subsequence Search

题目大意:给定一个长度为n的数组a,长度为m的数组b,n>=m,从数组a中找到每个长度为m的子序列,判断这个子序列和数组b相同的数是否大于等于k,如果是,则满足条件。

问有多少个满足上述条件的子序列

可以用滑动窗口的思想来思考这个题,不过没有那么复杂,只需要将数组a的前m个作为初始状态,不断将窗口向右移动,每次移动将左值弹出,右值加入。

然后对于每个状态的窗口,判断它里面的数是否和b数组相同

然后可以发现,只有初始化的时候需要每个都判断,之后的每次滑动,只需要判断左值和右值,中间的值都不会变,统计满足的数量即可。

        int n, m, k;
		cin >> n >> m >> k;
		map<int, int> mp, now;
		for (int i = 1; i <= n; i++) cin >> a[i], st[i] = 0;
		for (int i = 1; i <= m; i++) cin >> b[i], mp[b[i]]++;
		int num = 0, ans = 0;
		for (int i = 1; i <= m; i++) {
			now[a[i]]++;
			if (now[a[i]] <= mp[a[i]]) num++;
		}
		if (num >= k) ans++;
		for (int i = m + 1; i <= n; i++) {
			if (now[a[i - m]] <= mp[a[i - m]]) num--;
			now[a[i - m]]--;
 
			now[a[i]]++;
			if (now[a[i]] <= mp[a[i]]) num++;
			if (num >= k) ans++;
		}
//		for (int i = 1; i <= m; i++) {
//			now[a[i]]++;
//			if (mp[a[i]] != 0) mp[a[i]]--, st[i] = 1, num++;
//		}
//		if (num >= k) ans++;
//		for (int i = m + 1; i <= n; i++) {
//			now[a[i]]++;
//			now[a[i - m]]--;
//			if (st[i - m] == 1) {
//				mp[a[i - m]]++;
//				if (now[a[i - m]] >= mp[a[i - m]]) mp[a[i - m]]--;
//				else num--;
//			}
//			if (mp[a[i]] != 0) mp[a[i]]--, st[i] = 1, num++;
//			if (num >= k) ans++;
//		}
		cout << ans << '\n';

E. Long Inversions

题目大意:给定一个长度为n的字符串s,对于每个位置可以将该位置的字符向后延申到长度为k的子字符串,将该子字符串反转,即 ‘ 1 ’ - > ' 0 ' ,' 0 ' - > ' 1 '

判断最终字符串是否都可以变为‘ 1 ’ ,找到能满足条件的最大的k的值

因为数据范围很小,只有5000,第一步想到的是暴力,即每次遇到 ’0‘都进行一次反转,然后找到最大的k,很遗憾,超时了。

        int n;
		cin >> n;
		string s1;
		cin >> s1;
		string ss;
		for (int i = 1; i <= n; i++) ss += '1';
		int ans = 0;
		for (int k = 1; k <= n; k++) {
			string s = s1;
			for (int i = 0; i <= n - k; i++) {
				if (s[i] == '0') {
					for (int j = i; j < i + k; j++) {
						if (s[j] == '0') s[j] = '1';
						else s[j] = '0';
					}
				}
			}
			if (s == ss) ans = k;
		}
		cout << ans << '\n';

然后想一下,因为每次更新的都是一个区间,可以用差分的思想,将需要更新的状态进行不断的更新,而不是更新一个区间,最后将状态数组跑一遍即可。

        char check(char c, bool t) {
	        if (t == 0) return c;
	        else {
	        	if (c == '0') return '1';
	        	else return '0';
	        }
        }        
        int n;
		cin >> n;
		string s;
		cin >> s;
		string ss;
		for (int i = 0; i < n; i++) ss += '1';
		int ans = 0;
		for (int k = 1; k <= n; k++) {
			memset(st, 0, sizeof st);
			for (int i = 0; i < n; i++) {
				if (i != 0) st[i] = st[i - 1] ^ st[i];
				if (i > n - k) continue;
				if (check(s[i], st[i]) == '0') {
					//cout << k << ' ' << i << '\n';
					st[i] = !st[i];
					st[i + k] = !st[i + k];
				}
			}
			string c;
			for (int i = 0; i < n; i++) {
				c += check(s[i], st[i]);
			}
			//cout << c << '\n';
			if (c == ss) ans = k;
		}
		cout << ans << '\n';

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值