SDUT 2023 Summer Individual Contest - 3(for 22)

目录

B - Road to Arabella(博弈论)

D - Meeting Bahosain(gcd)

I. Bashar and Hamada

J - Thanos Power

M. Two Operations

B - Road to Arabella(博弈论)

题意:给一个n和k,每个人每次可以从max(1,n-k)中选一个数,然后把n减去这个数。n为0时游戏结束。把0摆在对方面前就是赢了。

题解:(遇到这种题时考虑一下必胜态,必败态会好思考一点)

如此我们很容易想起奇偶性博弈,总能把偶数摆在对方面前,我们就行了,所以对所有n,k;我们要做的是,能不能总是把偶数摆在对方面前。
当n=k+1或者n=k时,选的那个数只能为1。只要这时的n是偶数,那么每次轮到Ayoub选时n都是奇数,轮到Kilani选时n都是奇数,那么Ayoub是必胜的。

当n>k+1时Kilani永远是必胜态,因为他可以让自己选的时候n为奇数。

#include <bits/stdc++.h>
using namespace std;
#define pi 3.1415926
#define X first
#define Y second
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define endl "\n"
#define int long long
#define ULL unsigned long long
#define pb push_back
typedef pair<int, int> PII;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
const int N = 3e6 + 100, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7,P=133331;
void solve()
{
 int n;
	scanf("%d",&n);
	while(n--)
	{
		int a,b;
		cin>>a>>b;
		if(a==b||a-1==b)
		{
			if(a&1)printf("Kilani\n");
			else printf("Ayoub\n");
		}
		else printf("Kilani\n");
	}
  
} 
signed main()
{
	Ysanqian;
	int T;
	T=1;
	//cin >> T;
	while (T--)solve();
	return 0;
}
 

D - Meeting Bahosain(gcd)

题意:给定a,b数组,b数组不存在相同元素,进行以下操作,选一个a[i], 让其进行任意次的加减b数组的任意数,问是否可以让a数组全部相等

思路:设一个数x,a[i]-a[j]=x(i,j为任意下标),x=b[1]*x1+b[2]*x2+...+b[n]*xn,

然后好像就是扩展欧几里德了(可能说错了)

( 即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。

    换句话说,如果ax+by=m有解,那么m一定是gcd(a,b)的若干倍)

那么x=k*gcd(b1,b2,b3...bn),那么我们检查每个a[i]-a[j]是否可以整除gcd(b1,b2,b3,b4...bn)就可以了(其实只需枚举相邻的元素即可)

#include <bits/stdc++.h>
using namespace std;
#define pi 3.1415926
#define X first
#define Y second
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define endl "\n"
#define int long long
#define ULL unsigned long long
#define pb push_back
typedef pair<int, int> PII;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
const int N = 3e6 + 100, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7,P=133331;
int n,m;
bool flag;
int a[N],b[N];
int gcd(int a,int b)
{
  return b?gcd(b,a%b):a;
}
void solve()
{
   cin>>n>>m;
   for(int i=1;i<=n;i++)
   cin>>a[i];
   for(int i=1;i<=m;i++)
   cin>>b[i];
   int res=b[1];
   for(int i=2;i<=n;i++)
   res=gcd(res,b[i]);
   for(int i=2;i<=n;i++)
   {
    int dis=a[i]-a[i-1];
    if(dis%res!=0)
    {
      flag=1;
     break;
    }
   }
  if(flag)
  cout<<"No"<<endl;
  else 
  cout<<"Yes"<<endl;
} 
signed main()
{
	Ysanqian;
	int T;
	T=1;
	//cin >> T;
	while (T--)solve();
	return 0;
}
 

I. Bashar and Hamada

题意:给你一个 数组 a[N]让你选则元素大于2的子数组,使得子数组里面的所有数的差的绝对值最大。

思路:

1.首先n=2时,肯定选择数组中的最大值和最小值,这样F2=max-min

2.n=3时,在F2的基础上,无论选择哪个都是一样的,假设选取的数是x,F3=F2 + max - x + x - min = 2 * F2   

3.n=4时,在F3的基础上,随便取一个数 y, F4 =F3 + max - y + y - min +| y - x | = F3 + F2 + | y - x | ,要想使F4最大,x、y需要分别为剩下数中最小值和最大值。  

所以我们可以相到sort一下,最小最大依次选取,一定是最优的         

具体可以看一下这个

首先我们应该明白顺序是不影响大小的。然后再找一个规律:

假定一组数据 {1,2,3,4,5,7},开始ans=0;

2:开始:ans+= |1-7|

3:插入5: ans+=|5-1|+|5-7|=|1-7|

4:插入2: ans+=|2-1|+|2-7|+|2-5|==|1-7|+|2-5|

5:插入4: ans+=|4-1|+|4-7|+|4-5|+|4-2|==|1-7|+|2-5|

6:插入3: ans+=|3-1|+|3-7|+|3-5|+|3-2|+|3-4|==|1-7|+|2-5|+|3-4|

这样列出来就很明显了ans位递推的

偶数个加上当前未选过的max-min

奇数个则加上当前有那些max-min对

每次组合都选差最大的,排序后,就是左右各一个。

思路转载自这个博客:
原文链接:https://blog.csdn.net/qq_62802647/article/details/125626195

#include <bits/stdc++.h>
using namespace std;
#define pi 3.1415926
#define X first
#define Y second
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define endl "\n"
#define int long long
#define ULL unsigned long long
#define pb push_back
typedef pair<int, int> PII;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
const int N = 3e6 + 100, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7,P=133331;
int n,a[N];
void solve()
{
 cin>>n;
 for(int i=1;i<=n;i++)
 cin>>a[i];
 sort(a+1,a+1+n);
 int ans=0,sum=0;
 int l=1,r=n;
 for(int k=2;k<=n;k++)
 {
  if((k&1)==0)sum+=(a[r--]-a[l++]);
  ans+=sum;
  cout<<ans<<' ';
 }
} 
signed main()
{
	Ysanqian;
	int T;
	T=1;
	//cin >> T;
	while (T--)solve();
	return 0;
}

J - Thanos Power

题意:就是给你一个很大的数,让你从初始0经过两个操作分别得到这个数

两个操作为

1:增加一个10的x次方

2:减少一个10的x次方(x对应的其为第几位,例如1020中1对应的x为3)

其实就是让某一位加一或减一

这题当时没有看,后来补的

思路:对于每一位上的数i有两种的得到的方式:1:直接从0加到i,所需操作数为i .2:上一位加1,然后从此位上减,所需操作数为1+10-i,

f[i][j]:(j取0或1,0表示通过2操作,1表示通过1操作)

状态表示:dp[i][j]代表第0到第i位上的数采用j种方式所需的最小操(注意这里得上一位是前一个高位,我们从最大位开始循环)

状态计算:

1:如果这一维是从0上升得到的数字,我们这一位操作数就为s[i]-'0',我们要对上一位得操作取min所以f [ i ] [ 1 ] = min ⁡ ( f [ i − 1 ] [ 0 ] , f [ i − 1 ] [ 1 ] ) + s[i]-'0' 即可 ;

2:如果这一维是从高位下降得来的数,那么如果前一维也是下降的话,可以让前一维少下降一位,很明显这样做可以使得当前为得到这个数的步数更小。同理,如果前一位是从低位升上来的,这一维需要从高位降,那没有办法,只能让前一维多走一步(即前一位加一),再加上当前位数从高位降下来的值。d p [ i ] [ 0 ] = min ⁡ ( d p [ i − 1 ] [ 0 ] − 1 , d p [ i − 1 ] [ 1 ] + 1 ) + 10 − (s[ i ]- ' 0 ');

#include <bits/stdc++.h>
using namespace std;
#define pi 3.1415926
#define X first
#define Y second
#define Ysanqian ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define endl "\n"
#define int long long
#define ULL unsigned long long
#define pb push_back
typedef pair<int, int> PII;
int dx[4]={-1,1,0,0},dy[4]={0,0,-1,1};
const int N = 3e6 + 100, M = 1010, inf = 0x3f3f3f3f, mod = 1e9 + 7,P=133331;
int f[N][2];
string s;
void solve()
{
 cin>>s;
 int n=s.size();
 f[0][0]=11-(s[0]-'0');
 f[0][1]=s[0]-'0';
 for(int i=1;i<n;i++)
 {
   f[i][1]=min(f[i-1][0],f[i-1][1])+s[i]-'0';
   f[i][0]=min(f[i-1][0]-1,f[i-1][1]+1)+10-(s[i]-'0');
 }
  cout<<min(f[n-1][0],f[n-1][1])<<endl;
} 
signed main()
{
	Ysanqian;
	int T;
	T=1;
	//cin >> T;
	while (T--)solve();
	return 0;
}

M. Two Operations

题意:给你一个字符串和两个操作,求进位之后的字典序最大的答案。

1:交换任意两个字符的位置

2:你可以删除具有相同值的任意两个相邻字符,并按字母顺序将它们替换为下一个字符

例如aa可以变成b,但是zz不能在变了

这题你一开始想的用栈来写,毕竟变完之后还有可能和后面的依然相等这样也能过,但是太麻烦了

然后发现了另一种方法

思路:用个数组记录每个字母出现的次数,然后从低位开始进位,最后从高位到低位输出即可。

这样像就太简单了。。。

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+10;
int vis[N];
int main(){
    string s;
    cin>>s;
    int len=s.size();
    for(int i=0;i<len;i++){
        vis[s[i]-'a']++;//这里就变成记录0~25了(一共26个)我有的时候容易想错(蒻)
    }
    for(int i=0;i<25;i++){
        vis[i+1]+=vis[i]/2;
        vis[i]%=2;
    }
    for(int i=25;i>=0;i--){
        while(vis[i]--){
            cout<<char(i+'a');
        }
    }
    cout<<endl;
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值