2023牛客暑期多校训练营2

目录

D.The Game of Eating

E.The Game of Eating

F.Link with Chess Game

G.Link with Centrally Symmetric Strings

I.Link with Gomoku

K.Box


D.The Game of Eating

思路:考虑贪心。每个人都会选择一道对于自身价值最大的菜,但考虑到其他人会帮自己提供一定的贡献,即样例二,第一个只需要点第三道菜,第二个人点第四道菜,自动帮第一个人补全了第四道价值最大的菜,所以当前人的选择可能要考虑到后续人员的影响,既然如此,我们考虑最后一个人,即最后一个进行点菜时,没有人对其选择造成影响,然后依次往前推,每个人取当前的最大价值即可。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<int, 3> p3;
int mod = 51123987;
const int maxv = 4e6 + 5;

int st[2005];
int a[2005][2005];
void solve()
{
    memset(st,0,sizeof st);
    int n,m,k;
    cin>>n>>m>>k;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++) cin>>a[i][j];
    }
    int res=k%n;
    if(res==0) res=n;
    vector<int> ans;
    for(int i=res;i>=1;i--){
        int val=0;
        int c=0;
        for(int j=1;j<=m;j++){
            if(st[j]) continue;
            if(a[i][j]>val){
                val=a[i][j];
                c=j;
            }
        }
        st[c]=1;
        ans.push_back(c);
        if(i==1) i=n+1;
        k--;
        if(k==0) break;
    }
    sort(ans.begin(),ans.end());
    for(auto x: ans) cout<<x<<" ";
    cout<<endl;

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    //cout<<endl;
    system("pause");
    return 0;
}

E.The Game of Eating

思路:考虑去枚举每个k,因为k最多只能到18,数据范围很小,然后按照题意去模拟即可。

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=998244353;
const int maxv=4e6+5;

void solve()
{	
	ll n;
	cin>>n;
	ll x=10;
	for(int i=0;;i++){
		if(i==0) x=1;
		else x*=10;
        if(x*n>1e18) break;
		ll t=sqrtl(x*n);
		if(t*t/x==n) {
			cout<<t<<endl;
			return ;
		}
		if((t+1)*(t+1)/x==n){
			cout<<t+1<<endl;
			return; 
		}
	}
	cout<<-1<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
	return 0;
}

F.Link with Chess Game

思路:官方题解给定的是二分图博弈,以后有空在去了解吧。

当n为偶数时,先手必胜,当为奇数时,和r,g,b的状态相同,当r,g,b为偶数的时候先手必胜。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
typedef pair<int, int> pll;
typedef array<int, 3> p3;
int mod = 1e9 + 7;
const int maxv = 4e6 + 5;


void solve()
{
    int n,r,g,b;
    cin>>n>>r>>g>>b;
    if(n%2==0){
        cout<<"Alice"<<endl;
        return ;
    }
    else{
        if((r+g+b)%2==0){
            cout<<"Alice"<<endl;
            return ;
        }
    }
    cout<<"Bob"<<endl;

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    system("pause");
    return 0;
}

G.Link with Centrally Symmetric Strings

题意:题目给定关于回文对称的新定义,然后问是否可以将给定字符串拆解成若干个回文字符串。

思路:关于回文字符串,我们可以用马拉车算法处理出每个位置上的回文半径,然后设置一个变量f 在遍历整个字符串时去记录最长的不合法的长度,若当前位置的回文半径大于不合法长度,那么直接跳到i+f处。最后变量值若为0则表示为合法字符串。

接下来来证明为什么这么做是对的:

1 若给定字符串s不能被拆解成若干个回文对称字符串,即存在某一些位置不是回文对称的,那么从该位置开始,变量f值会一直大于后续的所有的回文半径。因为只要出现回文半径大于f的情况,f就会被置为0,重新开始。

2 若给定字符串能够被拆解成若干个回文对称字符串,那么每当匹配到回文对称中心时,此时的位置距离前一个回文字符串的结尾距离小于等于p[i],而此时的回文半径长度肯定是大于f的,所以会一直匹配下去。

#include <bits/stdc++.h>

using namespace std;
const int N = 1e6 + 10;
typedef long long ll;
typedef pair<int, int> pll;
typedef array<int, 3> p3;
int mod = 1e9 + 7;
const int maxv = 4e6 + 5;

int z[300];
int p[N*2];


void solve()
{
    string s;
    cin>>s;
    string a;
    a='#';
    for(int i=0;i<s.size();i++){
        a.push_back(s[i]);
        a.push_back('#');
    }
    a=" "+a;
    int n=a.size();
    for(int i=1;i<n;i++){
        if(z[a[i]]==a[i]) p[i]=1;
        else p[i]=0;
    }
    int r=0,m=0;
    for(int i=1;i<n;i++){
        if(i<r) p[i]=min(p[m*2-i],r-i+1);
        while(z[a[p[i]+i]]==a[i-p[i]]&&p[i]+i<n) p[i]++;
        if(i+p[i]-1>r){
            r=i+p[i]-1;
            m=i;
        }
    }
    int f=0;
    for(int i=1;i<n;i++){
        //cout<<p[i]<<" ";
        if(p[i]>f){
            i+=f;
            f=0;
        }
        else f++;
    }
    if(!f){
        cout<<"Yes"<<"\n";
    }
    else cout<<"No"<<"\n";

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    cin >> t;
    z['b']='q';
    z['q']='b';
    z['d']='p';
    z['p']='d';
    z['n']='u';
    z['u']='n';
    z['o']='o';
    z['s']='s';
    z['x']='x';
    z['z']='z';
    z['#']='#';
    while (t--)
    {
        solve();
    }
    system("pause");
    return 0;
}

思考:为什么是将当前位置加上变量f,而不是直接让i加上回文半径p:考虑下述情况:

 当我们将字符串的黄色部分遍历完后(保证此种情况l1的右界小于l2),此时f=0,当我们遍历到对称中心l1时,我们若直接让i加上对应的回文半径p[l1]时,会直接变为l2附近,那么后续会出现还有一半的蓝色部分不为回文字符串。若是加上对应的f,因为f的值肯定是小于对应回文半径的,所以在其回文半径内,即使半径进行减小,也为回文字符串。所以此时让i加上对应的f到蓝色字符串的开头即可。然后蓝色字符串匹配到l2出自然会结束匹配。

I.Link with Gomoku

题意:给定一个n*m的棋盘下五子棋,让构造出一个棋局为和棋。

思路:按照样例模拟即可。即放四个白子,一个黑子,下一行放四个黑子,一个百子,然后找题目要求去后续填充即可。

#include<bits/stdc++.h>

using namespace std;
const int N=1e5+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=998244353;
const int maxv=4e6+5;


void solve()
{	
	int n,m;
	cin>>n>>m;
	
		if(n%2){
		for(int i=1;i<=n-1;i++){
			if(i%2){
				for(int j=1;j<=m;j++){
					if(j%5==0) cout<<'o';
					else cout<<'x';
				}
			}
			else{
				for(int j=1;j<=m;j++){
					if(j%5==0) cout<<'x';
					else cout<<'o';
				}
			}
			cout<<endl;
		}
		for(int i=1;i<=m;i++){
			if(i%2) cout<<'x';
			else cout<<'o';
		}
		cout<<endl;
		}
		else{
		for(int i=1;i<=n;i++){
			if(i%2){
				for(int j=1;j<=m;j++){
					if(j%5==0) cout<<'o';
					else cout<<'x';
				}
			}
			else{
				for(int j=1;j<=m;j++){
					if(j%5==0) cout<<'x';
					else cout<<'o';
				}
			}
			cout<<endl;
		}
		}


}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	cin>>t;
	while(t--){
		solve();
	}
	system("pause");
	return 0;
}

K.Box

#include<bits/stdc++.h>

using namespace std;
const int N=1e6+5;
typedef long long ll;
typedef pair<ll,ll> pll;
int mod=998244353;
const int maxv=4e6+5;


ll dp[N][3];
ll a[N],b[N];


void solve()
{	
    int n;
    cin>>n;
    vector<int> pos;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    pos.push_back(0);
    for(int i=1;i<=n;i++){
        cin>>b[i];
        if(b[i]==1) pos.push_back(i);
    }
    memset(dp,-0x3f,sizeof dp);
    if(pos.size()==1){
        cout<<0<<endl;
        return ;
    }
    if(pos[1]-1>0) dp[1][0]=a[pos[1]-1];
    if(pos[1]+1<=n) dp[1][2]=a[pos[1]+1];
    dp[1][1]=a[pos[1]];
    for(int i=2;i<pos.size();i++){
        for(int j=0;j<3;j++){
            for(int k=0;k<3;k++){
                if(pos[i-1]+k-1<pos[i]+j-1&&pos[i]+j-1<=n){
                    dp[i][j]=max(dp[i][j],dp[i-1][k]+a[pos[i]+j-1]);
                }
            }
        }
    }
    int x=pos.size()-1;
    cout<<max({dp[x][0],dp[x][1],dp[x][2]})<<endl;
}

int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	//cin>>t;
	while(t--){
		solve();
	}
	system("pause");
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值