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 
#include 
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 
#include 
#include 
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 
#include 
#include 
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  
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  
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 
#include  
#include 
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 思路
(1)情况1:自身存在上升,则该数组与任意拼接都存在上升,即2*n-1种都上升。情况2:两两不存在上升,所以,只要存在前一个数组的最小值小于后一个数组的最大值即存在上升。
(2)对于每个序列我们开两个数组,一个记录序列的最大值,一个记录序列的最小值。特殊情况就是这个序列本身就有上升,那么它的最大值为无穷大,最小值为-1。
(3)给最大值排序,遍历数组如果最小值<最大值,即满足条件,贡献为:当前最大值的位置到末尾有多少个数累加起来即可。

#include 
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 
#include 
#include 
#include 
#include 
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,算法)