20241114升达ACM校队训练赛(部分题解)

20241114升达ACM校队训练赛

C - Kousuke’s Assignment

题意描述:

给出一个数组,计算最大的不重叠美丽段数量:

如果a[l]+a[l+1]+a[l+2]+…+a[r]=0,则该段被认为是美丽的。

解题思路:

如果数组中有一个数字为0,则无需和其他数字结合,本身便是美丽的

  1. 怎样判断哪一段是完美的?完美的符合怎样的条件

    数组:   1  2  3  -3  0  1  0  -1
    前缀和: 1  3   6  3  3  4   4   3
    

    我们可以发现如果一段是完美的,那么他的前缀和会与未加上该段时前缀和的值相等(因为完美段和为0)

  2. 如果数组中有一个数字为0,则无需和其他数字结合,本身便是美丽的

  3. 因此,我们只需要判断每次的前缀和是否出现过,可以用map来记录sum

  4. 由于不重复,每次判断出一个完美段后,需要将map清空以及sum归零

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    map<int,int>mp;
    void solve()
    {
    	mp.clear();
    	int n;
    	cin>>n;
    	int x;
    	int sum=0;
    	int ans=0;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>x;
    		sum+=x;
    		if(x==0||mp[sum]==1||sum==0)
    		{
    			ans++;
    			sum=0;
    			mp.clear();
    		}
    		else
    		mp[sum]=1;
    		
    	}
    	
    	cout<<ans<<endl;
    }
    signed main()
    {
    	IOS
    	int t;
    	t=1;
    	cin>>t;
    	while(t--)
    	{
    		solve();
    	}
    	return 0 ;
    }
    

D - Sakurako’s Field Trip

题意描述:

每次可以将数组中位置i和位置n-i+1的学生交换,可以进行无限次交换,使得数组中相邻数相同数量最小,输出可达到的最小数量。

解题思路:

  1. 如果n为2,则判断两个数是否相等,可以直接输出

  2. 如果大于2,可以从中间向两边以l,r为指向位置判断是否交换

  3. l,r怎么取值???

    如果n为奇数,则中间的不用变换

    如果n为偶数,我们可以发现中间的两个数字交换前后并无差别

    因此,l为中间数字(n为奇数,中间数字为1个,n为偶数,中间数字为2个)之前的位置,r为中间的数字后的位置

  4. 当什么时候进行交换?

    交换后比交换前相邻数的组数在减少时进行交换

    怎样判判断是否减少?

    e,b分别记录左右两侧交换前与中间靠中间一侧重复数字的个数,同时,c,d分别记录左右两侧交换后与中间靠中间一侧重复数字的个数,通过比较e+b和c+d来决定是否交换

  5. 最后输出数组中相邻数相同数量即可

    解题代码:

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    int a[100010];
    void solve()
    {
    	int n,e=0,b=0,c=0,d=0,t;
    	cin>>n;
    	
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	if(n==2)
    	{
    		if(a[1]==a[2])
    		cout<<"1"<<endl;
    		else
    		cout<<"0"<<endl;
    	}
    	else
    	{
    		int l,r;
    		if(n%2==0)
    		{
    			l=n/2-1;
    			r=n/2+2;
    		}
    		else
    		{
    			l=n/2;
    			r=n/2+2;
    		}
    		while(l>0&&r<=n)
    		{
    			if(a[l]==a[l+1])
    			e=1;
    			if(a[r]==a[r-1])
    			b=1;
    			if(a[l]==a[r-1])
    			c=1;
    			if(a[r]==a[l+1])
    			d=1;
    			if(e+b>c+d)
    			{
    				t=a[l];
    				a[l]=a[r];
    				a[r]=t;
    			}
    			e=0,b=0,c=0,d=0;
    			l--;r++;
    		}
    		int ans=0;
    		for(int i=2;i<=n;i++)
    		{
    			if(a[i]==a[i-1])
    			ans++;
    		}
    		cout<<ans<<endl;
    	}
    
    }
    signed main()
    {
    	IOS
    	int t;
    	t=1;
    	cin>>t;
    	while(t--)
    	{
    		solve();
    	}
    
    	return 0 ;
    }
    

    F - New Game

    题目描述:

    给出两个整数,n和k。n张卡牌,每张卡牌上有一个整数,当前回合他可以拿一张数字为 x 或数字为 x+1 的卡牌,所拿卡牌上写的不同数字的数量不得超过 k。确定在游戏中可以从牌堆中拿到的最大卡牌数量(他在第一回合可以从牌堆中拿任何一张卡牌)

    解题思路:

    1. 将所有卡牌从小到大排序,每个数字出现的次数存在map中
    2. 用l,r指针记录当前取出卡牌的最大值与最小值,防止超过k
    3. 类似于滑动窗口,保证所取卡牌不同牌数最大等于k,并且每次结果都与ans比较,取得最大值

    代码如下:

    #include <bits/stdc++.h>
    using namespace std;
    #define int long long
    #define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    const int N=2e5+10;
    int a[N];
    void solve()
    {
    	int n,k;
    	cin>>n>>k;
    	for(int i=1;i<=n;i++)
    	{
    		cin>>a[i];
    	}
    	sort(a+1,a+n+1);
    	map<int,int>m;
    	vector<int>v;
    	for(int i=1;i<=n;i++)
    	{
    		if(m[a[i]]==0)
    		v.push_back(a[i]);
    		m[a[i]]++;
    	}
    	int l=0,r=0;
    	int sum=0;
    	int ans=0;
    	while(1)
    	{
    		if(r>v.size()-1) break;
    		ans=max(ans,m[v[r]]);
    		sum+=m[v[r]]; 
    		r++;
    		while(v[r]-v[r-1]==1)
    		{
    			if(r-l>=k)
    			{
    				sum-=m[v[l]];
    				l++;
    			}
    			sum+=m[v[r]];
    			ans=max(sum,ans);
    			r++;
    		} 
    		
    		sum=0;
    		l=r;
    	}
    	cout<<ans<<endl;
    }
    signed main()
    {
    	IOS
    	int t;
    	t=1;
    	cin>>t;
    	while(t--)
    	{
    		solve();
    	}
    	return 0 ;
    }
    

    H - I Love 1543

    题目描述:

给出n*m的大墙毯(n,m均为偶数),顺时针遍历所有层,求出地毯所有层中 1543 1543 1543出现的总次数。

解题思路:

  1. 由于给出的数字并未用空格隔开,我们先进行处理,将其转换为二维数组
  2. 我们对最外层的四个方向数存入一个容器中,由于它属于一个环,因此应该将最初的3个数再次存入容器的尾部
  3. 对容器的数进行判断,求出 1543 1543 1543出现的次数
  4. 由于不止一层,每层判断方式一致,将上述步骤放入循环即可,循环次数即层数为min(m,n)/2

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
const int N=1010;
int a[N][N];
int ans;
void solve()
{
	ans=0;
	int m,n;
	cin>>m>>n; 
	string s;
	for(int i=1;i<=m;i++)
	{
		cin>>s;
		for(int j=1;j<=s.size();j++)
		{
			a[i][j]=s[j-1]-'0';
		}
	}
//	for(int i=1;i<=m;i++)
//	{
//		
//		for(int j=1;j<=s.size();j++)
//		{
//			cout<<a[i][j]<<" ";
//		}
//		cout<<endl;
//	}

	for(int k=1;k<=min(m,n)/2;k++)
	{
		vector<int>v;
		for(int j=k;j<=n-k+1;j++)
		{
			v.push_back(a[k][j]);
		}
		for(int i=k+1;i<=m-k+1;i++)
		{
			v.push_back(a[i][n-k+1]);
		} 
		for(int j=n-k;j>=k;j--)
		{
			v.push_back(a[m-k+1][j]);
		} 
		for(int i=m-k;i>k;i--)
		{
			v.push_back(a[i][k]);
		} 
		for(int i=0;i<3;i++)
		{
			v.push_back(v[i]);
		}
//		for(auto &x:v){
//			cout<<x<<" ";
//		} 
//		cout<<endl;
		
//	
		for(int i=0;i<v.size();i++)
		{
			if(i+3>=v.size())
			break;
			if(v[i]==1&&v[i+1]==5&&v[i+2]==4&&v[i+3]==3)
			ans++;
		}
	}

	cout<<ans<<endl;
}
signed main()
{
	IOS
	int t;
	t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0 ;
}

A - Reverse the Rivers

题目描述:

n n n个国家,每个国家有 k k k个地区,a[i][j]反映该地的水量。
智者打算为第 i个国家的第 j 个地区和第 (i+1)个国家的第 j个地区之间创建通道经过通道创建,地区的水量将由a[i][j]变为a[i][j]|a[i-1][j].
接下来给出q个要求,找出满足要求的最小国家的编号

解题思路:

  1. 处理二维数组,将其变为通道创建后的水量
  2. 异或后数组为递增形式,由于我们需要使用地区的查找,可以将其进行倒置放vector容器,方便接下来查找函数的使用
  3. 想要满足所有要求,需要注意应该取’>'中的最大值和‘<’中的最小值,与数学答案需要满足多组条件时,取相交类似
  4. 最终的区间[l,r]即为满足所有要求的值,如果l<=r,则取l的值即为最优,否则就不存在满足所有条件的值。输出“-1”即可
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//const int N=1e5+10;
//
//int sum=0;
void solve()
{
	int m,n,k;
	cin>>m>>n>>k;
	int a[m+1][n+1];
	vector<vector<int>>v(n+1,vector<int>(m+1)); 
	for(int i=1;i<=m;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cin>>a[i][j];
			if(i>1)
			a[i][j]|=a[i-1][j]; 
			v[j][i]=a[i][j];
		} 
		
	} 
	 while(k--)
	 {
	 	int p;
	 	cin>>p;
	 	int l=1,r=m;
		int x,y;
		char s;
	 	while(p--)
	 	{
		 	cin>>x>>s>>y;
		 	if(s=='>')
		 	{
		 		int u=upper_bound(v[x].begin()+1,v[x].end(),y)-v[x].begin();
				l=max(l,u);	
			} 
			else
			{
				int w=lower_bound(v[x].begin()+1,v[x].end(),y)-v[x].begin();
				r=min(r,w-1);
			}
		}
	 	if(l<=r)
	 	cout<<l<<endl;
	 	else
	 	cout<<"-1"<<endl;
	} 
}
signed main()
{
	IOS
	int t;
	t=1;
//	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0 ;
}

B - Alya and Permutation

题意描述:

构造包含n个数的排列p,最大化n次操作后操作后k的值,k的初始值为0.
执行n次操作,在第i次操作(i=1,2,3…n):
· 如果i是奇数,则k=k&p[i]其中 & 表示 按位与操作。
· 如果 i 是偶数,则k=k|p[i] ,其中 | 表示 按位或操作。

解题思路:

  1. 如果为偶数个,我们将2 1输出接下来按序输出即为所求
  2. 奇数个时,k通过&与|操作后的最大值,应该为n转化为二进制后的数位,并且各个位全为1转化为10进制的数,(例如10(1010)最大可变为15(1111))
  3. 奇数个时怎样操作才能使得最后结果为与n二进制相同位数的1呢?
    以10为例,我们想得到1111,我们可以1010&1011得到,111又可以由110|11得到,而111又可以由1得到,将这些放到最后,其他的各个位则无所谓
  4. 但是,6为特例,他会导致前后重复,将其单独输出即可

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
//const int N=1e5+10;
//
//int sum=0;
int a[200010];
void solve()
{
	int n;
	cin>>n;
	if(n==6)
	{
		cout<<"7"<<endl;
		cout<<"1 2 4 6 5 3"<<endl;
		return;
	 } 
	if(n%2!=0)
	{
		int k=0;
		a[1]=2,a[2]=1;
		for(int i=3;i<=n;i++)
		{
			a[i]=i;
		} 
		for(int i=1;i<=n;i++)
		{
			if(i%2==0)
			{
				k|=a[i];
			}
			else
			k&=a[i];
		}
		cout<<k<<endl;
		for(int i=1;i<=n;i++)
		{
			cout<<a[i]<<" ";
		} 
		cout<<endl;
	 } 
	 
	else
	{
			int p;
		for(int i=1;i<=n;i++)
		{
			if(pow(2,i)>n)
			{
				p=i;
				break;
			}
		}
		cout<<pow(2,p)-1<<endl;
		int m=pow(2,p-1);
		cout<<"2 ";
		for(int i=4;i<=n;i++)
		{
			if(i!=m-1&&i!=m-2&&i!=m)
			cout<<i<<" ";
		} 
		cout<<"1 3 "<<m-2<<" "<<m-1<<" "<<m<<endl;
	}
	
}
signed main()
{
	IOS
	int t;
	t=1;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0 ;
}

G - Kar Salesman

题意描述:

商店有n种不同型号汽车,第i种有a[i]辆,每位顾客最多买x辆汽车(且这x辆为不同型号),求出可以卖出所有汽车需要的最少顾客量

解题思路:

我们在每一次的减去最大数目型号的过程中,都会去删除其他的值,也就是次大值,然后就得到了一个答案,要不然就是最大值,或者是sum/x向上取整的答案

  1. 如果按汽车不同型号搭配卖出,则为sum%x==0?sum/x+1:sum/x;
  2. 否则为最多的汽车数量数目,取两者最大值

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define int long long
int a[500010];
void solve()
{
	int m,n;
	int ma=0;
	int sum=0;
	cin>>m>>n;
	for(int i=1;i<=m;i++)
	{
		cin>>a[i];
		ma=max(ma,a[i]);
		sum+=a[i];
	}
	int p;
	if(sum%n==0)
	p=sum/n;
	else
	p=sum/n+1;
	cout<<max(ma,p)<<endl; 
}
signed main()
{
	int	t;
	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
} 

E - Black Cells

题意描述:

选择两个白色单元格i,j(i!=j&&abs(i-j)<=k),并将它们涂成黑色。给定一个列表a,该列表中的所有单元格必须被涂成黑色。此外,最多只能 涂一个不在此列表中的单元格。你的任务是确定使这一切成为可能的最小值 k。

解题思路:

  1. 如果n为1,则直接输出1
  2. 如果n为偶数,则无需借助其他单元格便可以涂完所有单元格,我们取最大差值即可
  3. n为奇数时,我们应该将奇数位置上的与其他单元格结合,如果偶数位置的改动,则会使偶数前后的数结合,使得差值更大,我们依次尝试每个奇数位置的变换,取最小的一个

代码如下:

#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[2010];

void solve() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	if (n == 1) {
		cout << "1" << endl;
		return;
	} else if (n % 2 == 0) {
		int ma = 0;
		for (int i = 2; i <= n; i += 2) {
			ma = max(ma, a[i] - a[i - 1]);
		}
		cout << ma << endl;
		return;
	} else {
		int res = 1;
		int ans = 1e18;
		for (int i = 1; i <= n; i += 2) {
			res = 1;
			for (int j = 1; j <= n; j += 2) {
				if (j == i) {
					j--;
					continue;
				}

				res = max(res, a[j + 1] - a[j]);
			}
			ans = min(ans, res);
		}
		cout << ans << endl;
	}
}

signed main() {
	int	t;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值