[题解]CF958F1.Lightsabers (easy)[codeforces 04]

题目描述

There is unrest in the Galactic Senate. Several thousand solar systems have declared their intentions to leave the Republic. Master Heidi needs to select the Jedi Knights who will go on peacekeeping missions throughout the galaxy. It is well-known that the success of any peacekeeping mission depends on the colors of the lightsabers of the Jedi who will go on that mission.

Heidi has 𝑛n Jedi Knights standing in front of her, each one with a lightsaber of one of 𝑚m possible colors. She knows that for the mission to be the most effective, she needs to select some contiguous interval of knights such that there are exactly 𝑘1k1​ knights with lightsabers of the first color, 𝑘2k2​ knights with lightsabers of the second color, ...... , 𝑘𝑚km​ knights with lightsabers of the 𝑚m -th color. Help her find out if this is possible.

输入格式

The first line of the input contains 𝑛n ( 1<=𝑛<=1001<=n<=100 ) and 𝑚m ( 1<=𝑚<=𝑛1<=m<=n ). The second line contains 𝑛n integers in the range 1,2,...,𝑚1,2,...,m representing colors of the lightsabers of the subsequent Jedi Knights. The third line contains 𝑚m integers 𝑘1,𝑘2,...,𝑘𝑚k1​,k2​,...,km​ (with 

) – the desired counts of lightsabers of each color from 11 to 𝑚m .

输出格式

Output YES if an interval with prescribed color counts exists, or output NO if there is none.

输入输出样例
  • 输入#1

    5 2
    1 1 2 2 1
    1 2
    

    输出#1

    YES
题目理解:

这是一个典型的区间子序列问题,与颜色分配相关。我们可以将其看作是一个滑动窗口的问题,其中我们需要找到一个连续的子序列(区间),使得每个颜色的lightsaber数量满足给定的要求。

首先,我们需要理解题目的关键点:

  1. 颜色分布:我们有 𝑛n 个Jedi Knight,他们的lightsaber颜色由 𝑚m 种可能的颜色组成。
  2. 目标分布:我们需要找到一个区间,使得这个区间内每种颜色的lightsaber数量分别是 𝑘1,𝑘2,...,𝑘𝑚k1​,k2​,...,km​。
  3. 滑动窗口:我们可以使用滑动窗口的概念来遍历整个序列,窗口的大小可以变化,但必须是连续的。

解题步骤可以分为以下几步:

  1. 初始化:创建一个大小为 𝑚m 的计数数组 count,用于记录当前窗口中每种数量。同时,创建一个与 count 相同大小的 target 数组,存储每种颜色的目标数量 𝑘𝑖ki​。

  2. 滑动窗口

    • 初始化一个左边界 left 和一个右边界 right,初始时 left = right = 0
    • 使用一个循环,每次移动右边界 right,直到它到达序列末尾。
    • 对于每个右边界,更新 count 数组,增加对应颜色的计数。
    • 检查当前窗口的 count 是否与 target 相匹配。如果匹配,输出 "YES" 并结束。
    • 如果不匹配,移动左边界 left,减少对应颜色的计数。注意,需要确保左边界不越过右边界。
  3. 结束:如果遍历完整个序列都没有找到匹配的子序列,输出 "NO"。

在实际编程实现时,可以考虑使用双指针法,一边扩展窗口一边检查条件,这样可以提高效率。同时,对于颜色计数的更新,可以使用哈希映射或者前缀和等数据结构来优化。

代码:

老样子先给出网络代码:CF958F1 Lightsabers (easy) 题解 - 心海秋的墨木仄 - 博客园 (cnblogs.com)

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define fr(i , a , b) for(ll i = a ; i <= b ; ++i)
#define fo(i , a , b) for(ll i = a ; i >= b ; --i)
#pragma comment(linker , "/stack : 200000000")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
using namespace std;
inline char gchar()
{
    static char buf[1000000] , *p1 = buf , *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 1000000 , stdin) , p1 == p2) ? EOF : *p1++;
}
inline ll read()
{
    ll x = 0 , f = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9')
	  {
        if(ch == '-')
        {
        	f = -1;
		}
        ch = getchar();
    }
    while(ch >= '0' && ch <= '9')
	  {
        x = (x << 1) + (x << 3) + (ch ^ 48);
        ch = getchar();
    }
    return x * f;
}
ll n , m;
ll a[101] , b[101];
ll t[101] , flag;
signed main()
{
    n = read();
    m = read();
    fr(i , 1 , n)
    {
		a[i] = read();
	}
	fr(i , 1 , m)
	{
		b[i] = read();
	}
	fr(len , 1 , n)
	{
		fr(from , 1 , n)
		{
			flag = 0;
			fr(i , 1 , m)
			{
				t[i] = 0;
			}
			ll to = from + len - 1;
			fr(i , from , to)
			{
				t[a[i]]++;
			}
			fr(i , 1 , m)
			{
				if(t[i] != b[i])
				{
					flag = 1;
					break;
				}
			}
			if(!flag)
			{
				printf("YES");
				return 0;
			}
		}
	}
	printf("NO");
    system("pause");
    return 0;
}

接下来让我们用AI分析这段代码,看看结果有没有更优解:

(以下为AI分析):

你的代码已经正确地实现了题目的要求,通过遍历所有可能的区间并检查颜色计数来判断是否存在满足条件的连续子序列。但是,我们可以优化这段代码,使其运行更快,尤其是在面对大输入时。以下是一些优化建议:

  1. 避免不必要的计算:在你的代码中,fr(len, 1, n) 和 fr(from, 1, n) 两层循环进行了交叉遍历,导致一些重复计算。实际上,我们只需要从每个颜色开始,检查向右的所有连续子序列。这可以通过使用一个变量 curr 来跟踪当前颜色的计数,并在遍历过程中更新它。

  2. 使用滚动数组:在计算每个子序列的颜色计数时,可以使用滚动数组来减少空间复杂度。例如,你可以维护两个数组 t1 和 t2,分别用于前一个子序列和当前子序列的颜色计数。当移动到下一个子序列时,t2 可以直接从 t1 更新,而不是每次都初始化为零。

  3. 提前结束循环:一旦发现某个颜色计数不匹配,可以立即结束当前子序列的检查,不需要检查剩余的颜色。下面是我修改的代码:

    #include <iostream>
    using namespace std;
    
    int n, m;
    int a[101], b[101];
    int t1[101], t2[bool check(int from, int len) {
        for (int i = 1; i <= m; ++i) {
            t2[i] = t1[i];
        }
    
        for (int i = from; i < from + len; ++i) {
            t2[a[i]]++;
        }
    
        for (int i = 1; i <= m; ++i) {
            if (t2[i] != b[i]) {
                return false;
            }
        }
        return true;
    }
    
    int main() {
        cin >> n >> m;
        for (int i = 1; i <= n; ++i) {
            cin >> a[i];
        }
        for (int i = 1; i <= m; ++i) {
            cin >> b[i];
        }
    
        for (int i = 1; i <= m; ++i) {
            t1[a[i]]++;
            if (check(i, m)) {
                cout << "YES" << endl;
                return 0;
            }
        }
    
        cout << "NO" << endl;
        return 0;
    }

    但是本题中限制了m,n的范围,所以在本题中这种优化可能效果不大

  • 12
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值