Codeforces 简单思维题合集

codeforces 1285B Just Eat It!

https://codeforces.ml/problemset/problem/1285/B
题意 :有两个人,n堆蛋糕,每个蛋糕有ai的权值,第一个人全部拿走,第二个人取片段(这个片段不能是[1,n])。比较谁的权值大,如果第一个人的权值大于第二个人则输出YES,否则输出NO。
思路:维护一个最大子段。但由于不能全部取完,所以需要从1—n-1遍历一遍。再从2—n遍历一遍取最大值。
代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll inf=999999999;
ll a[100005];
int main(){
	int t,n;
	cin>>t;
	while(t--){
		cin>>n;
		ll sum=0;
		for(int i=0;i<n;i++){
			cin>>a[i];
			sum+=a[i];
		}
		ll ans=-inf,now=0;
		for(int i=0;i<n-1;i++){
			now+=a[i];
			ans=max(ans,now);
			if(now<0) now=0;
		}
		now=0;
		for(int i=1;i<n;i++){
			now+=a[i];
			ans=max(ans,now);
			if(now<0) now=0;
		}
		if(sum>ans) cout<<"YES"<<endl;
		else cout<<"NO"<<endl;
	}
	return 0;
}

codeforces 1178B. WOW Factor

https://codeforces.ml/problemset/problem/1178/B
题意:两个连续的v可以组合成w,问字符串中有多少个可以组成 wow 的子序列。
思路:计算o之前w的个数 l,和o的个数 t,那么之后的一个w可以与之前的ans个wo组成一个wow。具体来说,当找到一个w时,计入一个左边的w即l++,同时当它作为右边的w的时候ans+=t,这个t是当遇到o时,计算的wo的数目,即t+=l。
代码

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll inf=999999999;
string s;
int main(){
	cin>>s;
	ll ans=0,l=0,t=0;
	for(int i=1;i<s.size();i++){
		if(s[i]=='v'&&s[i-1]=='v'){
			ans+=t;
			l++;
		}
		if(s[i]=='o') t+=l;
	}
	cout<<ans<<endl;
	return 0;
}

codeforces 1176C. Lose it!

https://codeforces.ml/problemset/problem/1176/C
题意:题目把 [4,8,15,16,23,42] 定义为一个好的子序列,然后给一个数组,可以对数组进行无数次操作(删除一个元素),问最少操作多少次,可以将该数组变成好的序列(保证数组的每个 [4,8,15,16,23,42] 序列的顺序,相互之间可以交叉)
思路:先找有多少个这样的子序列,ans=n-6*子序列的个数,在线处理,记录6个数分别出现的次数,当出现一次完整的序列后,整体cnt–,注意在计数时,只有当前一个数的数量大于后一个数的数量时,才可计数。
代码

#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=500005;
const ll inf=999999999;
int a[maxn],cnt[10];
int b[6]={4,8,15,16,23,42};
int main(){
	int n;
	cin>>n;
	int num=0;
	for(int i=0;i<n;i++){
		cin>>a[i];
		if(a[i]==4) cnt[0]++;
		for(int j=1;j<6;j++){
			if(a[i]==b[j]&&cnt[j-1]>cnt[j]){
				cnt[j]++;
			}
		}
		if(cnt[0]&&cnt[1]&&cnt[2]&&cnt[3]&&cnt[4]&&cnt[5]){
			num++;
			for(int j=0;j<6;j++){
				cnt[j]--;
			}
		}
	}
	cout<<(n-6*num)<<endl;
	return 0;
}

codeforces 1040B. Shashlik Cooking

https://codeforces.com/problemset/problem/1040/B
题意:给出一个n和一个k,有n个烤串需要翻面,如果翻i那么[i−k,i+k],[i−k,i+k]全部都要翻面,问最少翻多少次可以把n个烤串全部翻面,以及怎么翻。
思路:这道题用到了贪心的思想。需要尽量让每次翻转发挥最大的贡献,首先让第一个翻面,然后每隔2*k个之后再翻一次,这样保证了除了第一个剩下的都是最优的。由于第一个烤串已经翻了k个,所以最后剩下没有翻转的烤串一定小于等于k个,于是我们把所有的翻转点右移即可。因为小于k个,第一个又是反转的,所有右移后依旧可以保证全部翻转。
代码

#include <iostream> 
using namespace std;
int main(){
	int n,k,ans=0,r;
	cin>>n>>k;
	ans=n/(2*k+1);
	r=n%(2*k+1);
	if(r!=0) ans++;
	else r=2*k;
	cout<<ans<<endl;
	for(int i=r/2+1;i<=n;i+=(2*k+1)){
		cout<<i<<" ";
	}
	cout<<endl;
	return 0;
}

codeforces 1395C. Boboniu and Bit Operations

https://codeforces.com/contest/1395/problem/C
题意:给出有n个元素的数组a,和m个元素的数组 b,求c数组,c[i]=a[i] & b[j] (j ∈ [1,m]),使得 c1| c2 | …… | cn 最小
思路:假设答案是A. 那么对于所有 i (1≤i≤n), ci|A=A。因为ai,bi<2^9, 可以枚举答案0 to 2^9−1, 检查是否存在 j 对于每个 i , (ai&bj)|A = A. 最小的就是最终的答案。
代码

#include <iostream> 
using namespace std;
int a[205],b[205];
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=0;i<n;i++){
		cin>>a[i];
	}
	for(int i=0;i<m;i++){
		cin>>b[i];
	}
	for(int t=0;t<(1<<10);t++){
		int flag1=1;
		for(int i=0;i<n;i++){
			int flag2=0;
			for(int j=0;j<m;j++){
				if((t|(a[i]&b[j]))==t){//注意优先级,加括号
					flag2=1; break;
				}
			}
			if(!flag2){
				flag1=0; break;
			}
		}
		if(flag1){
			cout<<t<<endl;
			break;
		}
	}
	return 0;
}

codeforces 798B. Mike and strings

https://codeforces.com/problemset/problem/798/B
题意:给个 n 个串,每次我们能将一个串的第一个字符移到该串末尾,问我们至少要移动多少次,才能使所有串相同。
思路:暴力枚举,找到所有串的最长连续公共子串,将串左边的移到右边。具体为,枚举每个串,假设该串为最终相同的串,在双倍的每个串中找到这个串,则要移动的即为左边多出来的字符个数。
代码

#include <iostream>
#include <algorithm> 
#include <string>
using namespace std;
const int inf=999999999;
string s[55];
int main(){
	int n;
	cin>>n;
	for(int i=0;i<n;i++){
		cin>>s[i];
	}
	int ans=inf;
	for(int i=0;i<n;i++){
		int flag=1,sum=0;
		for(int j=0;j<n;j++){
			string t=s[j]+s[j];
			if(t.find(s[i])==t.npos){//未找到该串
				flag=0; break;
			}
			else sum+=t.find(s[i]);
		}
		if(flag) ans=min(ans,sum);
	}
	if(ans==inf) cout<<-1<<endl;
	else cout<<ans<<endl;
	return 0;
}

codeforces 1284B. New Year and Ascent Sequence

https://codeforces.com/problemset/problem/1284/B
题意:给你n个序列,任选两个序列串接,序列p和序列q连接,p+q,使ai<aj (1<=i<j<=n)。问你有多少个连接。
思路
(1)情况1:自身存在上升,则该数组与任意拼接都存在上升,即2*n-1种都上升。情况2:两两不存在上升,所以,只要存在前一个数组的最小值小于后一个数组的最大值即存在上升。
(2)对于每个序列我们开两个数组,一个记录序列的最大值,一个记录序列的最小值。特殊情况就是这个序列本身就有上升,那么它的最大值为无穷大,最小值为-1。
(3)给最大值排序,遍历数组如果最小值<最大值,即满足条件,贡献为:当前最大值的位置到末尾有多少个数累加起来即可。
代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+5;
const int inf=0x3f3f3f3f;
vector<int> maxs,mins;
int main(){
	int n,l,s;
	cin>>n;
	while(n--){
		cin>>l;
		int mx=-inf,mi=inf,flag=0;
		for(int i=1;i<=l;i++){
			cin>>s;
			if(s>mi){
				flag=1;
			}
			mx=max(mx,s);
			mi=min(mi,s);
	    }
	    if(flag){
	    	mins.push_back(-1);
	    	maxs.push_back(inf);
		}
		else{
			mins.push_back(mi);
			maxs.push_back(mx);
		}
	}
	ll ans=0;
	sort(maxs.begin(),maxs.end());
	for(int i=0;i<mins.size();i++){
		ans+=(maxs.end()-upper_bound(maxs.begin(),maxs.end(),mins[i]));
	}
	cout<<ans<<endl;
	return 0;
}

codeforces 1282B1. K for the Price of One (Easy Version)

https://codeforces.com/problemset/problem/1282/B1
(转自https://www.cnblogs.com/pixel-Teee/p/12098715.html)
题意:Vasya去商店购买物品,她可以购买k件物品,只需支付其中最昂贵的那一件就可以了,或者直接单件购买。她有p个硬币,给出n件物品的价格,需要保证购买每件物品的时候,剩余的钱要大于这件物品的价格。
思路一:可以采用贪心策略,尽量用钱包里的钱去单买便宜的物品,那么就可以买的越多,然后用另一种方式去购买昂贵的物品。我们先排序一下,那么所有物品的价格就会从小到大排序,然后枚举单买的前k - 1件物品,从0 ~ k - 1,为什么不枚举到k呢?因为枚举到k了,那么采用第二种方式的价格更优。
代码

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	int t;
	scanf("%d", &t);
	while (t--)
	{
		int n, p, k;
		scanf("%d%d%d", &n, &p, &k);
		vector<int> a(n);
		for (int i = 0; i < n; ++i){
			scanf("%d", &a[i]);
		}
		sort(a.begin(), a.end());
		int ans = 0, now = 0, cnt = 0, pre = 0;
		//pre:前i个物品的总价格
		//枚举前k - 1个单买,剩下的用第二个技能买
		for (int i = 0; i < k; ++i)
		{
			now = pre;
			cnt = i;
			//钱包钱不够
			if (p < now){
				break;
			}
			for (int j = i + k - 1; j < n; j += k){
				now += a[j];
				if (now <= p){
					cnt += k;
				}
				else
					break;
			}
			ans = max(ans, cnt);
			//累加该物品,作为下次循环使用
			pre += a[i];
		}
		printf("%d\n", ans);
	}
	return 0;
}

思路二:可以采用DP策略,首先前提是,如果我们买了第i件物品的话,那么第i - 1物品的价格应该小于这件物品,显然这样购买的顺序是最优解,因此我们先排序一下。
可以看出,这是一道背包问题,我们把f[i]表示为购买前i件物品所需的最小价钱,那么因为存在两种购买方式,f[i]可以由这两种方式转移过来,f[i]可以由f[i - 1] + a[i]转移过来,或者
可以由f[i - k] + a[i]转移过来,其中有k - 1物品不需要支付价格。那么如果得到的f[i] <= p,那么它的数量就可以更新ans这个记录答案的变量。

codeforces 1276A. As Simple as One and Two

https://codeforc.es/problemset/problem/1276/A
题意:给出一串字符串。去掉最少的字符,使这个字符串不能有连续的“one”,“two"出现,保证输入的都是小写字母。输出去掉的最小的字符个数和去掉字符的位置(索引从1开始)。
思路:删除一个字符后,它后面的字符就会补上来,可能再次形成不合法,比如字符串twooo删除第三个字符o,但是后面的又是o,显然这样是不可行的。我们应该选择删除第二个字符w,而不是two的第三个字符o。
具体可以分成以下三种情况:
1.“…one…” -> 删除第二个n
2.“…two…” -> 删除第二个w
3.”…twone…" -> 删除第三个o
其实2,3是一种情况,当我们遇到two要判断一下是第二还是第三种情况。
代码

#include <bits/stdc++.h> 
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
int inx[maxn];
int main(){
	int t;
	cin>>t;
	while(t--){
		string s;
		cin>>s;
		int k=0,n=s.size();
		for(int i=1;i<n-1;i++){
			if(s[i-1]=='o'&&s[i]=='n'&&s[i+1]=='e'){
				inx[k++]=i; s[i]='x';
			}
			if(s[i-1]=='t'&&s[i]=='w'&&s[i+1]=='o'){
				if(i+3<n&&s[i+2]=='n'&&s[i+3]=='e'){
					inx[k++]=i+1; s[i+1]='x';
				}
				else{
					inx[k++]=i; s[i]='x';
				}
			}	
		}
		cout<<k<<endl;
		for(int i=0;i<k;i++){
			cout<<inx[i]+1<<" ";
		}
		cout<<endl;
	}
	return 0;
}

codeforces 1215B. The Number of Products

https://codeforces.com/problemset/problem/1215/B
题意:给你一个值都不为零的数组,分别找出有多少个连续的子串乘积小于零,大于零。
思路:dp1[i] 表示以a[i]为区间末尾区间积为正的区间左端点数目;dp2[i]表示以a[i]为区间结尾区间积为负的区间左端点数目。遍历数组,当a[i]为正时,更新dp1[i] = dp1[i-1]+1,dp2[i]继承dp2[i-1]; 当a[i]为负时, 更新dp1[i] = dp2[i-1], dp2[i]=dp1[i-1]+1。
代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=2e5+5;
const int inf=0x3f3f3f3f;
ll a,dp1[maxn],dp2[maxn];
int main(){
	ll n,ans1=0,ans2=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a;
		if(a>0){
			dp1[i]=dp1[i-1]+1;
			dp2[i]=dp2[i-1];
		}
		else{
			dp1[i]=dp2[i-1];
			dp2[i]=dp1[i-1]+1;
		}
		ans1+=dp1[i];
		ans2+=dp2[i];
	}
	cout<<ans2<<" "<<ans1<<endl;
	return 0;
}
  • 6
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值