2023年中国大学生程序设计竞赛女生专场(ccpc女生赛)

 A.疾羽的救赎

思路:这道题的思路其实非常清晰,题意也很好懂,如果说有个关键点的话就是把每个棋子当前的位置做一下标记,我这里设置的是now数组,然后用一个deque数组当作棋盘,每一个元素上面都可以摞棋子(因为可以双端出入),这里的话,主要是考虑到进出的顺序,从尾出从头进,就可以不乱顺序。主体就是通过now找到某个棋子的位置,通过位置定位到棋盘上的某个格子找到该双端队列的元素,取出,然后放到相应的位置(注意如果之前有棋子的话要摞到之前棋子的上面)。

#include "iostream"
#include "vector"
#include "deque"
using namespace std;
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int t;
	cin>>t;
	while(t--){
		int cnt = 12;
		int a,b;
		int now[4] = {0,2,3,4};//下标表示颜色,值用来表示某个颜色的棋子的当前位置     
		deque<int> v[20];//表示棋盘
		for(int i = 1; i <= 3; i++)
			v[now[i]].push_back(i);	
		while(cnt --)
		{
			cin>>a>>b;
			int flag = 0;
	  		deque<int> &t = v[now[a]];
	  		deque<int> u;
			while(t.back() != a)
				u.push_front(t.back()), now[t.back()] +=b, t.pop_back();
			u.push_front(t.back()),now[t.back()] +=b, t.pop_back();
			while(!v[now[a]].empty())
			{
				u.push_front(v[now[a]].back());
				v[now[a]].pop_back();
			}
			v[now[a]]=u;  
			 
		}
		if(v[9].size() == 3) cout<<"Y"<<endl;
		else cout<<"N"<<endl;
	}
	
	return 0;
}

B. 终焉之茧

这道题就是一个交互题,二分求点,这里参考了大佬的答案

2023CCPC女生赛 女生专场题解 ABFGHKL(更新至7题)-优快云博客

主要就是读懂题意,然后是题目每次评判给的都是你给的要传送的距离,并不是坐标,然后就是比较左右两边哪一边离终点比较近,然后找到最近的,x轴和y轴分别进行二分查找就可以了。因为2的30次方很大,所以基本不会超过。

#include "iostream"
using namespace std;
typedef long long ll;
ll l = -1000, r = 1000;
ll nowx, nowy;
ll ask(ll x, ll y){
	cout<<x - nowx<< ' '<< y - nowy<<endl;
	nowx = x, nowy = y;
	fflush(stdout);
	ll ans;
	cin>>ans;
	return ans;
}
int main(){
	ll d, mid;
	cin>>d;
	if(d == 0) return 0;
	ll d1 = ask(l,0);
	if(d1 == 0) return 0;
	ll d2 = ask(r,0);
	if(d2 == 0) return 0;
	while(l < r){
		if(d1 < d2){
			mid = l + r >> 1;
			d2 = ask(mid, nowy);
			if(d2 == 0) return 0;
			r = mid;
		}
		else if(d1 > d2){
			mid = l + r  + 1>> 1;
			d1 = ask(mid, nowy);
			if(d1 == 0) return 0;
			l = mid;
		}
		else{
			mid = l + r >> 1;
			if(ask(mid, nowy) == 0 ) return 0;
			break;
		}
 
	}
	l = -1000, r = 1000;
	d1 = ask(nowx, l);
	if(d1 == 0) return 0;
	d2 = ask(nowx, r);
	if(d2 == 0) return 0;
	while(l < r){
		if(d1 < d2){
			mid = l + r >> 1;
			d2 = ask(nowx, mid);
			if(d2 == 0) return 0;
			r = mid;
		}
		else if(d1 > d2){
			mid = l + r  + 1>> 1;
			d1 = ask(nowx, mid);
			if(d1 == 0) return 0;
			l = mid;
		}
		else{
			mid = l + r >> 1;
			if(ask(nowx,mid) == 0 ) return 0;
			break;
		}
 
	}
	return 0;
}

 F. 最长上升子序列

实际上在赛场上做这题的时候我们甚至都还不知道什么叫拓扑排序,之后上数据结构课才知道,当成一个思维题来做的,当时离结束就半小时左右了吧做的这道题,我用了两分钟就写完了,其实主题思路就是从最小的那个递增子序列长度从一开始排列,碰到一样长度的就从后往前排列(这样可以保证递增序列长度不变),然后当递增的序列断档也就是不连续的时候该序列不合法,很容易明白的道理。见下图:

 

#include "iostream"
#include "vector" 
using namespace std;
const int N = 1e7;
vector<int > v[N];
int a[N], b[N];
int main(){
	int n;
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	int max = 0, flag = 0;
	cin>>n;
	for(int i = 1; i <= n; i++){
		cin>>a[i];
		if(a[i] > max) 
		{
			if(a[i] - max > 1) flag = 1;
			max = a[i]; 
		}
		v[a[i]].push_back(i);
	}
	if(flag) cout<<"-1"<<endl;
	else
	{
		int p = 1;
		for(int i = 1; i <= max; i++)
			for(int j = v[i].size() - 1; j >= 0; j --)
			{
				int t = v[i][j];
				b[t] = p ++;
			}
		
		for(int i = 1; i <= n; i++)
			cout<<b[i]<<' ';
		cout<<endl;
	}
	
	return 0;
	
}

 G. 精灵宝可梦对战

就是一个大模拟,具体的见我之前的博客,

ps:直接提交cf的话把注释删一下,否则会提示不是简单文本~~

G. 精灵宝可梦对战-优快云博客

#include "iostream"
#include "queue"
#include "vector"
#include "algorithm"
using namespace std;
struct BK{
	int H,A,B,C,D,E,W,e;	
};//属性比较多,所以考虑用结构体,一个BK代表一个宝可梦 
int h,a,b,c,d,e,w,ee;
queue<BK> AA,BB;  //AA和BB 表示小A和小B   ps:比较形象 
int main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);  //关流很重要,否则会超时 
	int n,m,k,cnt1 = 0, cnt2 = 0;
	cin>>n>>m>>k;
	while(n--){
		cin>>h>>a>>b>>c>>d>>e>>w;
		BK x ;
		x.H = h, x.A = a, x.B = b, x.C = c, x.D = d, x.E = e, x.W = w, x.e = 0;
		AA.push(x);
		
	} //把小A的宝可梦载入 
	while(m--){
		cin>>h>>a>>b>>c>>d>>e>>w;
		BK x;
		x.H = h, x.A = a, x.B = b, x.C = c, x.D = d, x.E = e, x.W = w, x.e= 0;
		BB.push(x);
		
	}  //把小B的宝可梦载入 
			BK aa = AA.front();
			BK bb = BB.front();			
			k *= 2;
			long long count = 0;   //aa是攻击方,bb是被攻击方 
		while(!AA.empty() && !BB.empty() && k > 0){	//攻击过程,结束条件是某一个人的宝可梦死光或者局数用完	 
				long long hit,  p, q;
				p = aa.A - bb.C >  0? aa.A - bb.C :0;
				q = aa.B - bb.D >  0? aa.B - bb.D :0;
				hit = max(p, q);
				if(aa.W > hit && aa.e >=  aa.E) hit = aa.W, aa.e -= aa.E;
				if(hit == p || hit == q) aa.e ++;	//模拟攻击,找伤害,一定注意是实际伤害最大的,别忘了能量的变化			 
				bb.H -= hit;
								
			    AA.push(aa);
			    AA.pop(); 
			    aa = AA.front(); //攻击方换发 
									                  
				if(bb.H <= 0)
				{
					BB.pop();
					bb = BB.front();//被攻击方死了才能换下一个				 	
				}
				swap(aa,bb);
				swap(AA,BB); //A和B换发 
				count ++;
				k--;			 
			}
		if(k >= 0 && !AA.empty() && !BB.empty()) cout<<"Draw"<<endl; //没死光就是平局						
		else if(count % 2 == 0) cout<<"Bob"<<endl;    //总是A是单数攻击,B是双数攻击,所以看结束的时候的攻击局数就可以判断谁赢了 
		else if(count % 2 == 1) cout<<"Alice"<<endl;
	return 0;
	}
	

 H. 字符串游戏

就是一个AC自动机的板子,具体看我之前的博客

H. 字符串游戏-优快云博客

#include <queue>
#include <cstdlib>
#include <cmath>
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <algorithm>
#include "map"
using namespace std;
typedef long long ll;
const int maxn =  2*1e6+9;
long long xx = 0;
long long len;
int trie[maxn][26]; //字典树
int cntword[maxn];  //记录该单词出现次数
int fail[maxn];     //失败时的回溯指针
int cnt = 0;
long long modd = 1e9 + 7; 
map<int, int> mp;
void insertWords(string s){
    int root = 0;
    for(int i=0;i<s.size();i++){
        int next = s[i] - 'a';
        if(!trie[root][next])
            trie[root][next] = ++cnt, mp[cnt] = i + 1;
        root = trie[root][next];
    }
    cntword[root]++;      //当前节点单词数+1
}
void getFail(){
    queue <int>q;
    for(int i=0;i<26;i++){      //将第二层所有出现了的字母扔进队列
        if(trie[0][i]){
            fail[trie[0][i]] = 0;
            q.push(trie[0][i]);
        }
    }
 
							//fail[now]    ->当前节点now的失败指针指向的地方
							//tire[now][i] -> 下一个字母为i+'a'的节点的下标为tire[now][i]
    while(!q.empty()){
        int now = q.front();
        q.pop();
 
        for(int i=0;i<26;i++){      //查询26个字母
            if(trie[now][i]){
                //如果有这个子节点为字母i+'a',则
				//让这个节点的失败指针指向(((他父亲节点)的失败指针所指向的那个节点)的下一个节点)
                //有点绕,为了方便理解特意加了括号
 
                fail[trie[now][i]] = trie[fail[now]][i];
                q.push(trie[now][i]);
            }
            else//否则就让当前节点的这个子节点
                //指向当前节点fail指针的这个子节点
                trie[now][i] = trie[fail[now]][i];
        }
    }
}
 
 
void query(string s){
    int now = 0,ans = 0;
    for(int i=0;i<s.size();i++){    //遍历文本串
        now = trie[now][s[i]-'a'];  //从s[i]点开始寻找
        for(int j=now;j ;j=fail[j]){
            //一直向下寻找,直到匹配失败(失败指针指向根或者当前节点已找过).
            ans += cntword[j];
            if(cntword[j] > 0) xx =(xx +  (i - mp[j] + 2) * (len - i) ) % modd;
            //cntword[j] = -1;    //将遍历国后的节点标记,防止重复计算
        }
    }
//    return ans;
}
 
int main() {
    int n, m;
    string s;
    cin >> n >> m;
    for(int i=0;i<n;i++){
        cin >> s ;
        insertWords(s);
    }
    fail[0] = 0;
    getFail();
    while(m --){
    	xx = 0;
    	cin >> s ;
	    len = s.length() ;
	    query(s) ;
	    cout<<xx<<endl;
	}
    
    return 0;
}

 K. RSP

水题 1/n,别问,问就是水。

#include "iostream"
using namespace std;
int main(){
	long long n,m;
	cin>>n>>m;
	cout<<"1/"<<n<<endl;
	return 0;
}

L. 养成游戏

这道题是月月在赛场上做出来的,思维清晰,手忙脚乱就是她(略略略)

以下是月月的原话:

“ 就是,就是暴力嘛,每一种属性都有0到K种选择,求出得分最高的一套选择就一个个试,试n的K次方次就可以了。最难是6的8次方,所以可以暴力;
   dfs从第一个属性开始从0—K赋值,接着第二个属性…一直递归到第K个属性,证明赋完一套值了,就check一下这套属性的最终得分;将每一套方案都计算完了,maxx里面存的也就是最高的得分了。需要注意的是,评委的评分可能不小,得开longlong,以及每套属性评分时都是固定的,比如这个评委用到的A3是x,另一个评委用到的A3也要是x,不能随便更改

#include<bits/stdc++.h>
#define N 1000000
#define int long long
using namespace std;
int I[N],J[N],op[N],a[N],b[N],d[N],v[N],A[N]; 
int n,m,K;
int maxx=0;
int check(){
	int sum=0;
	for(int i=0;i<m;i++){
		if(op[i]==0&&a[i]*A[I[i]]+b[i]*A[J[i]]<=d[i]) sum+=v[i];
		if(op[i]==1&&a[i]*A[I[i]]+b[i]*A[J[i]]>=d[i]) sum+=v[i]; 
	}
	return sum;
}
void dfs(int x,int y){
	A[x]=y;
	if(x==n){
		maxx=max(maxx,check());
		return ;
	}
	for(int i=0;i<=K;i++){
		dfs(x+1,i);
	}
	return ;
}
main(){
	ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); 
	cin>>n>>m>>K;
	 for(int i=0;i<m;i++){
	 	cin>>I[i]>>J[i]>>op[i]>>a[i]>>b[i]>>d[i]>>v[i];
	 }
	 for(int i=0;i<=K;i++){
	 	dfs(1,i);
	 }
	  cout<<maxx<<endl;
	  
}

总结:

这次是抱着拿奖的心态去了,也因为是第一次参加这么大型的比赛,敲代码手都不利索了,还好队长花花和月月临危不乱,斩下四题,充满遗憾,就差一点就拿奖了,下次继续努力吖!!!

评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小竹子14

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值