Codeforces Round #627 (Div. 3)

本文详细解析了Codeforces Round #627 (Div. 3)中的四道题目,包括A题的Tetris问题,通过判断数组中奇偶数的数量来确定是否可行;B题的回文子序列,通过寻找相邻相等元素解决;C题的青蛙跳跃,寻找R之间的最大距离;D题的Topic配对,利用尺取法或二分法求解。每道题的重点在于理解题意并运用合适的算法思维。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A - Yet Another Tetris ProblemCodeForces - 1324A

题意:给定一串长度为n的数组,当这串数组至少有一个数组大于 0 时进行如下操作:1、选择一个数+2 2、当这串数组每个数都大于 0 时,使这个数组每个数都减 1 ,直到有数小于等于0.然后在进行 1 操作,循环进行。问最后能否经过这些操作让这个数组上的数全部变为 0 。

思路:我感觉我当时写的时候思路是乱的,毕竟也好久没有写过题了,尴尬…….其实这个题就是思维题,你会发现只要数组同时出现奇数和偶数就不行,因为如果两个数相差 1 ,加上 2 是无法弥补的。

简单代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int main(){
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    int t, n, num, f;
    cin >> t;
    while( t-- ){
        cin >> n;
        int flag = 1;
        for( int i = 0; i < n; i++ ){
            cin >> num;
            if( !i ){
                f = num % 2;
            }
            else{
                if( f != ( num % 2 ) ){
                    flag = 0;
                }
            }
        }
        if( flag ){
            cout << "YES\n";
        }
        else{
            cout << "NO\n";
        }
    }
    return 0;
}

 


复杂代码(有点模拟的感觉,其实思路是乱的,碰巧过了罢了):

#include<iostream>
#include<algorithm>
using namespace std;
int main(){
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    int t, n, a[110];
    cin >> t;
    while( t-- ){
        cin >> n;
        int f = 0;
        for( int i = 0; i < n; i++ ){
            cin >> a[i];
            if( a[i] > 0 ){
                f = 1;
            }
        }
        int k = 1;
        while( f ){
            f = 0;
            sort( a, a + n );
            if( ( a[n - 1] - a[0] ) % 2 != 0 ){
                f = 0;
                k = 0;
            }
            a[0] += 2;
            sort( a, a + n );
            int tp = a[0];
            for( int i = 0; i < n && k; i++ ){
                a[i] -= tp; //避免使用while增加复杂度
                if( a[i] > 0 ){
                    f = 1;
                }
            }
         //   cout << a[0] << a[1] << a[2] << endl;
        }
        int p = 1;
        for( int i = 0; i < n; i++ ){
            if( a[i] != 0 ){
                p = 0;
                cout << "NO\n";
                break;
            }
        }
        if( p ){
            cout << "YES\n";
        }
    }
    return 0;
}

 



B - Yet Another Palindrome Problem

 题意:给定一个长度为n的数组,判断这个数组是否存在长度大于等于 3 的子序列,这个子序列满足是个回文串。

思路:我们可以发现不管多长的回文串,从中间开始往两边同时遍历组成的串都是回文串,所以我们只需要找到3个数,满足 a X a 的形式即可,也就是找到左右相邻边上的数相等的数就可以满足条件。因为这道题数都不大,可以选择暴力查找右边a。

答案代码:

#include<iostream>
#include<algorithm>
using namespace std;
const int N = 5e3 + 10;
int t, n, a[N];
bool sh( int d, int num ){
    for( int i = d; i < n; i++ ){
        if( a[i] == num ){
            return true;
        }
    }
    return false;
}
int main(){
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    cin >> t;
    while( t-- ){
        cin >> n;
        for( int i = 0; i < n; i++ ){
            cin >> a[i];
        }
        int f = 1;
        for( int i = 1; i < n - 1; i++ ){
            if( sh( i + 1, a[i - 1] ) ){
                cout << "YES\n";
                f = 0;
                break;
            }
        }
        if( f ){
            cout << "NO\n";
        }
    }
    return 0;
}

 



C - Frog Jumps

 题意:给定一个字符串,且这个字符串只包含 'L'(向左跳) and 'R'(向右跳)(从 1 开始编号),一只青蛙从 0号 开起跳,问能跳到 n + 1号 位置的最小每次跳跃距离d。注意每次跳跃的距离只能小于等于d。

思路:这道题其实就是一个思维题,试想每次能推动青蛙往前的就是R,而如果跳到L,最后的结果就是跳到距离这个L最近的上一个R,这个可以直接看成从上一个R到下一个R,所以最后就变成R之间的最大距离(才可以保证到达 n + 1 ),而为了处理边界问题可以把 0号 和 n + 1号变成R.

答案代码:

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2e5 + 10;
char s[N];
int main(){
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    int t;
    cin >> t;
    while( t-- ){
        cin >> s + 1;
        s[0] = 'R';
        int slen = strlen( s ); 
        // 注意用 char 数组最好使用 strlen函数(计算数组中真正有值的个数)
        s[slen] = 'R';
        int dis = -1, np = 0;
        for( int i = 1; i < slen + 1; i++ ){
            if( s[i] == 'R' ){
                dis = max( dis, i - np );
                np = i;
            }
        }
        cout << dis << "\n";
    }
    return 0;
}

 


 

​​​​​​​D - Pair of Topics

 题意:给定长度为n的两个数组,计算 ai + aj > bi + bj( i < j )的 ( i,j )个数。

思路:(尺取法)很明显这道题要对上面的式子变形,令ci = ai - bi, 讲上面等式变形为 ci + cj > 0;然后排序,令l为左端点,r为右端点,如果c[r] + c[l] > 0,则[l+1,r]这个区间都与 r 满足关系,然后r–-,如果c[r] + c[l] < 0,都不满足关系,则l++。尺取法的时间复杂度为O(logn) 但是快排的复杂度为O(nlogn)。

(二分)令ci = ai - bi, 讲上面等式变形为 ci + cj > 0;满足这个式子,从前往后枚举ci,那么就无关i<j,将c数组升序排列,如果ci>0 则后面的全部满足,如果ci <= 0 则用二分找恰好大于|ci|的位置,这个位置以及后面的数都满足。时间复杂度为 O(n*logn)。

答案代码(取尺):

#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;
ll a[N];
int main(){
    ios :: sync_with_stdio( false );
    cin.tie( NULL );
    ll n, num;
    cin >> n;
    for( ll i = 0; i < n; i++ ){
        cin >> a[i];
    }
    for( ll i = 0; i < n; i++ ){
        cin >> num;
        a[i] -= num; //可节省空间
    }
    sort( a, a + n );
    ll l = 0, r = n - 1, ans = 0;
    while( l < r ){
        if( a[l] + a[r] > 0 ){
            ans += r - l; //如果满足那么 l + 1 与 r 满足;l + 2 与 r 满足……
            --r;
        }
        else{
            ++l;
        }
    }
    cout << ans;
    return 0;
}

 


 

答案代码(二分):

#include<cstdio>
#include<cmath> 
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 5;
int c[maxn], a[maxn];
void solve(){
	int n, b;
	scanf("%d", &n);
	for (int i = 0; i < n; ++i){
		scanf("%d", &a[i]);
	}
	for (int i = 0; i < n; ++i){
		scanf("%d", &b);
		c[i] = a[i] - b;
	}
	sort(c, c + n);
	ll ans = 0;
	//极端情况,如果ci都大于0,用组合数来表示是maxn * (maxn - 1) / 2.这个数要超int
	int i; 
	for (i = 0; i < n; ++i){
		if (c[i] > 0){
			break;
		}
		int pos = upper_bound(c, c + n, abs(c[i])) - c;
		ans += n - pos;
	}
	//满足组合数,直接利用组合数计算即可。这里有个坑点就是需要注意数据类型溢出
	ans += (ll)(n - i) * (n - i - 1) / 2;
	printf("%lld\n", ans);
}
int main(){
	solve();
	return 0;
}

 


 

总结:有时候可以通过题目的时间限制条件来推断使用什么方法,多写思维题,其实不难发现上面很多题都是思维题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值