桂林电子科技大学第三届ACM程序设计竞赛

本文精选了桂电比赛中的几道题目解析,包括模拟、DP、BFS、后缀数组、拓扑排序等算法应用,如H题的字符串操作、I题的动态规划、J题的广度优先搜索、A题的后缀数组实现及F题的图论问题解决。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

很久没有写题解了。

今年桂电比赛又开始了,本来想比初赛的结果在高铁上就没有写。拖了两天来补个题解吧。

(题解顺序是解题顺序)

H题 分离

https://ac.nowcoder.com/acm/contest/558/H

签到题,纯粹模拟,没什么难度。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
int main(){
	int n;
	string s1;
	cin>>n;
	while(n--){
		cin>>s1;
		for(int i = 0 ; i < s1.length() ; i+=2)
			cout<<s1[i];
		cout<<endl;
	}
	return 0; 
} 

B题 重复

https://ac.nowcoder.com/acm/contest/558/B

会map容器的,应该一分钟。不会的可以用的方法也有很多,比如转成哈希表,字符串最小表达(我不知道这个会不会TLE)。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1e9;
map<string,bool> mp;
int main(){
	int n,m;
	cin>>n>>m;
	string s;
	while(n--){
		cin>>s;
		mp[s] = 0;
	}
	cout<<mp.size()<<endl;
	return 0; 
} 

I题 选择

https://ac.nowcoder.com/acm/contest/558/I

这是一道很明显的DP题。选取ai后不能选ai+1。

所以每个位置有两个状态,假设0:不选该数,1:选取该数

那么DP方程就显而易见dp[i][0] = max(dp[i-1][1],dp[i-1][0])

                                          dp[i][1] = max(dp[i-1][0]+ai,dp[i-1][1])

然后遍历一遍就OK了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 105;
int a[_max];
int dp[_max][2];
int main(){
	int T,n;
	cin>>T;
	while(T--){
		cin>>n;
		memset(dp,sizeof(dp),0);
		for(int i = 1 ; i <= n ; i++) cin>>a[i];
		for(int i = 1 ; i <= n ; i++){
			dp[i][0] = max(dp[i-1][1],dp[i-1][0]);
			dp[i][1] = max(dp[i-1][0]+a[i],dp[i-1][1]);
		}
		cout<<(dp[n][0],dp[n][1])<<endl;
	}
	return 0;
}

J题 相聚

这题和去年蓝桥杯的海岛题目很像。BFS搜索,vis做记号就行了。很久没写题目了,很傻逼的最开始mp开的int弄得我一直在想怎么输入不了。。。傻逼了。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 55;
char mp[_max][_max];
bool vis[_max][_max];
int n,m;
int dx[]={0,1,0,-1};
int dy[]={1,0,-1,0};
struct node{
	int x,y;
	void init(int x,int y){
		this->x = x;
		this->y = y;
	}
};
bool IsOut(node n1){
	if(n1.x > n || n1.x < 1) return false;
	if(n1.y > m || n1.y < 1) return false;
	if(vis[n1.x][n1.y]) return false;
	return true;
}
void bfs(int x,int y){
	queue<node> q;
	while(!q.empty()) q.pop();
	node now,next;
	now.init(x,y);
	q.push(now);
	vis[now.x][now.y] = 1;
	while(!q.empty()){
		now = q.front();
		q.pop();
		for(int i = 0 ; i < 4 ; i++){
			next.init(now.x+dx[i],now.y+dy[i]);
			if(!IsOut(next)) continue;
			vis[next.x][next.y] = 1;
			q.push(next);
		}
	}
	return ;
}
int main(){
	int T,cnt;
	cin>>T;
	while(T--){
		memset(vis,false,sizeof(vis));
		cnt = 0;
		cin>>n>>m;	
		for(int i = 1 ; i <= n ; i++)
			for(int j = 1 ; j <= m ; j++){
				cin>>mp[i][j];	
				if(mp[i][j] == '0') vis[i][j] = 1;
			}
		for(int i = 1 ; i <= n ; i++) 
			for(int j = 1 ; j <= m ; j++){
				if(vis[i][j]) continue;			
				cnt++;
				bfs(i,j);
			}
		cout<<cnt<<endl;		
	} 
	return 0;
}

E题 区间

这个题目有人说他用上升子序列做TLE了。这里说一下平常的DP中求得最长上升子序列和这道题是不一样的。最长上升子序列要求的不是一个连续的子序列。这里明显是了。这也是一道模拟题,自己理清一下思路,也很简单的。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll _max = 1005;
int a[_max];
int main(){
	int T,n;
	cin>>T;
	while(T--){
		cin>>n;
		for(int i = 1 ; i <= n ; i++) cin>>a[i];
		int l = 1,r = 1;
		int l_ans = 1,r_ans = 1,len = 0;
		for(int i = 2 ; i <= n ; i++){
			if(a[i] >= a[i-1]) r = i;
			else{
				if(len < r-l){
					l_ans = l;
					r_ans = r;
					len = r-l;
				}
				l = i;
			}
		}
		if(len < r-l){
			l_ans = l;
			r_ans = r;
		} 
		cout<<l_ans<<' '<<r_ans<<endl;
	}
	
	return 0;
}

A题 串串

这是一道模板题,如果不知道后缀数组的自然很难做,知道了就是一个标准模板题,所以直接放代码就好了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 100005;

char ch[MAXN], All[MAXN];
int SA[MAXN], Rank[MAXN], Height[MAXN], tax[MAXN], tp[MAXN], a[MAXN], n, m; 
char str[MAXN];
//Rank[i] 第i个后缀的排名; SA[i] 排名为i的后缀位置; Height[i] 排名为i的后缀与排名为(i-1)的后缀的LCP
//tax[i] 计数排序辅助数组; tp[i] Rank的辅助数组(计数排序中的第二关键字),与SA意义一样。
//a为原串
void RSort() {
    for (int i = 0; i <= m; i ++) tax[i] = 0;
    for (int i = 1; i <= n; i ++) tax[Rank[tp[i]]] ++;
    for (int i = 1; i <= m; i ++) tax[i] += tax[i-1];
    for (int i = n; i >= 1; i --) SA[tax[Rank[tp[i]]] --] = tp[i]; 
} 

int cmp(int *f, int x, int y, int w) { return f[x] == f[y] && f[x + w] == f[y + w]; } 

void Suffix() {
    //SA
    for (int i = 1; i <= n; i ++) Rank[i] = a[i], tp[i] = i;
    m = 127 ,RSort(); 
    for (int w = 1, p = 1, i; p < n; w += w, m = p) { 
        for (p = 0, i = n - w + 1; i <= n; i ++) tp[++ p] = i; 
        for (i = 1; i <= n; i ++) if (SA[i] > w) tp[++ p] = SA[i] - w;
        RSort(), swap(Rank, tp), Rank[SA[1]] = p = 1;
        for (i = 2; i <= n; i ++) Rank[SA[i]] = cmp(tp, SA[i], SA[i - 1], w) ? p : ++ p;
    }
    int j, k = 0;
    for(int i = 1; i <= n; Height[Rank[i ++]] = k) 
        for( k = k ? k - 1 : k, j = SA[Rank[i] - 1]; a[i + k] == a[j + k]; ++ k);
}

void Init() {
    scanf("%s", str);
    n = strlen(str);
    for (int i = 0; i < n; i ++) a[i + 1] = str[i];
}

int main() {
    Init();
    Suffix();
    ll ans = 0;
    for(int i = 1 ; i <= n ; i++) ans += n-SA[i]+1-Height[i];
    printf("%lld\n",ans);
	return 0;  
}

C题 二元

这题其实我很蠢,首先我是想二分答案来做,然后太久没写代码了。二分想了会,尴尬,然后TLE了。然后重新思考了下数据范围觉得二分答案确实不可行,然后就觉得那肯定是模拟了。就先排序了一边,想着用a为规则从大到小排序,然后前k个全选就有了一个可选内最大的a,然后根据最后要求的a+b最大值的条件来替代已选的元素。那就肯定要队列了。然后因为替换的是最小的就是优先队列了。既然一开始排序是a规则,那优先队列就是b了。想通了就OK了。

#include<bits/stdc++.h>
#define inf 1e9+7; 
using namespace std;
typedef long long ll;
const int _max = 1e5+5;
struct node{
	int a,b;
	bool operator < (const node &n1)const{
		return b > n1.b;
	} 
}n[_max];
bool cmp(node n1,node n2){
	return n1.a > n2.a;
}
int main(){
	priority_queue<node> q;
	int N,k;
	cin>>N>>k;
	for(int i = 1 ; i <= N ; i++) cin>>n[i].a>>n[i].b;
	sort(n+1,n+N+1,cmp);
	for(int i = 1 ; i <= k ; i++) q.push(n[i]);
	int ans = n[k].a + q.top().b;
	for(int i = k+1 ; i <= N ; i++){
		q.pop();
		q.push(n[i]);
		if(ans < n[i].a+q.top().b){
			ans = n[i].a+q.top().b;
		}
	}
	cout<<ans<<endl;
	return 0;
}

F题 点对

这道题目,首先我骂自己是个憨憨,改BUG改了二十分钟,全是莫名其妙的错误,怕不是到了饭点想吃饭了?

分析一下题目,找对点,有对点代表有环,这个很明显的。一个n个节点的环,那么他的对点有n*(n-1)/2对,这个应该挺容易推的就不推了。所以我们要做的就是首先建图,然后把非环的点排除掉。这个地方既然想到了环,自然而然就肯定要想到拓扑,然后我们首先预处理这个图,然后非环的节点排除,然后我们要做的就是把环与环之间分开来。每个环的权值是n*(n-1)/2。然后最后加起来就OK了。

#include<bits/stdc++.h>
#define inf 1e9+7; 
using namespace std;
typedef long long ll;
const int _max = 305;
vector<int> mp[_max];
vector<int> mp1[_max];
int in_degree[_max],out_degree[_max];
int vis_p[_max];
bool vis_mp[_max][_max];
int dfs(int s,int cnt){
	vis_p[s] = 2;
	for(int i = 0 ; i < mp[s].size() ; i++){
		int e = mp[s][i];
		if(vis_p[e] == 1 || vis_mp[s][e]) continue;
		vis_mp[s][e] = 1;
		cnt += dfs(e,vis_p[e] == 0);
	}
	return cnt;
} 
int main(){
	int n,m,u,v;
	cin>>n>>m;
	memset(in_degree,0,sizeof(in_degree));
	memset(out_degree,0,sizeof(out_degree));
	memset(vis_p,0,sizeof(vis_p));
	memset(vis_mp,0,sizeof(vis_mp));
	for(int i = 1 ; i <= m ; i++){
		cin>>u>>v;
		mp[u].push_back(v);
		mp1[v].push_back(u);
		in_degree[v]++;
		out_degree[u]++;
	}	
	for(int i = 1 ; i <= n ; i++){
		if(!vis_p[i] && (!in_degree[i] || !out_degree[i])){
			vis_p[i] = 1;
			if(!in_degree[i]){ 
				out_degree[i] = 0; 
				for(int k = 0 ; k < mp[i].size() ; k++){
					int s = mp[i][k];
					in_degree[s]--;
				}
			}
			else{ 
				in_degree[i] = 0;
				for(int k = 0 ; k < mp1[i].size() ; k++){
					int s = mp[i][k];
					out_degree[s]--;
				}
			} 
			i = 0;
		}
	}	
	int ans = 0;
	for(int i = 1 ; i <= n ; i++){
		if(vis_p[i]) continue;
		int ret = dfs(i,1);
		ans += ret*(ret-1)/2;
	}
	cout<<ans<<endl;
	return 0;
}

做到现在大概已经做了将近三个半小时吧,很久没做题真是老了很多。还是要好好写题啊(呵呵,我就说说,毕设不要做了?)。还有两题就明天写了。晚上可能还要看看英语。加油吧。

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

补说一下,我很绝望的查了一下F题,然后看到了一个人的解法。我突然觉得我很傻逼。只有300的数据范围。为什么不暴力呢。对不起,我是个睿智。

### 关于电子科技大学第八届ACM趣味程序设计竞赛第二场正式赛的题目解析 目前未找到针对电子科技大学第八届ACM趣味程序设计竞赛第二场正式赛的具体题解文档或官方解析[^1]。然而,可以通过分析常见的ACM竞赛题型以及类似的解题思路来推测可能涉及的内容。 #### 常见ACM竞赛题型及其解决方法 1. **暴力求解与优化** 许多ACM竞赛中的问题可以从暴力求解入手,逐步优化至更高效的算法。例如,《算法竞赛入门经典第二版》第七章提到的从暴力到枚举排列的方法可以帮助理解如何通过生成子集和回溯法解决问题[^2]。 2. **字符串处理与模式匹配** 类似于引用[2]中描述的“猜数字游戏”,需要编写一个程序比较两个四位数并输出特定格式的结果(如`xAyB`)。这类问题通常涉及到字符串操作、条件判断以及循环结构的设计[^3]。 3. **动态规划与状态转移方程** 动态规划是一种常用的技巧,尤其适用于具有重叠子问题特性的场景。比如计算某个位置可达路径总数时需考虑是否存在障碍物影响最终结果,正如引用[5]所展示的例子那样[(3,1)]无法获取金币因为后续移动受限[^4]。 4. **组合数学与计数原理应用** 对于某些统计类问题,则需要用到基本的加法规则或者乘法规则来进行有效数目估算。像引用[3]里关于OJ平台可免费使用的合适难度题目数量定义方式就是基于简单的位运算累加得出结论[^5]。 以下是部分典型代码片段示例: ```python def sum_of_digits(number): """返回整数number各位数字相加之和""" total = 0 while number > 0: total += number % 10 number //= 10 return total # 测试函数sum_of_digits() print(sum_of_digits(233)) # 输出应为8 ``` 上述Python脚本展示了如何快速计算任意正整数各个位上的数值总合,这正是应对类似a_i=a[i]=Σd_j形式需求的有效手段之一。 --- #### 总结 尽管未能直接定位到目标赛事的确切解答资料,但借助已知资源仍能够构建起一套完整的思考框架用于指导实际参赛过程中的策略制定工作。希望以上分享对你有所帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值