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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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;
}
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这个记录答案的变量。