滁州学院2021年“新年杯”程序设计大赛暨ACM算法创新实验室选拔赛题解

A、滁大七十周年校庆 — 趣味竞答

签到题,直接暴力模拟即可。

#include

using namespace std;

int main(){
    
    int t;
    
    //输入t
    cin >> t;
    
    //处理t组数据
    while(t--){
    	//ans用来统计结果
        int ans = 0;
        int l, r;
        //输入l和r
        cin >> l >> r;
        
        //从l循环到r 遍历区间内的所有数字
        for(int i = l; i <= r; ++i){
            
            //数位分离 统计当前数字的各数位上有多少个3
            int temp = i;
            while(temp){
                if(temp % 10 == 3){
                    ans++;
                }
                temp /= 10;
            }
            
            //统计当前数字最多能被3整除的次数
            temp = i;
            while(temp){
                if(temp % 3 == 0){
                    ans++;
                }else{
                	//除到不能被3整除为止
                    break;
                }
                temp /= 3;
            }   
            
        }
        
        //输出结果
        cout << ans << endl;
        
    }
	
	return 0;
}

B、滁大七十周年校庆 — 我全都要

根据题意可以马上判断出这是一个考察拓扑排序的问题,如果给出的依赖关系可以成功进行拓扑排序(即给出的依赖关系为一个有向无环图),小轩就可以拿走所有的奖品。还有重要的一点就是拓扑排序需要依赖队列结构,C++选手可以直接使用C++中的queue容器,C语言选手需要自己使用数组模拟一个队列结构。

#include 
#include 
#include 

using namespace std;

const int N = 110, M = 310;

int h[N], ne[M], e[M], idx;
int cnt[N];
int n, m;

//加边
void add(int a, int b){
	e[idx] = b;
	ne[idx] = h[a];
	h[a] = idx++;
}

//拓扑
bool top_sort(){
	queue q;

	for(int i = 1; i <= n; ++i){
		if(cnt[i] == 0){
			q.push(i);
		}
	}
	
	int sum = 0;
	
	while(q.size()){
		int t = q.front();
		q.pop();
		sum++;
		
		for(int i = h[t]; i != -1; i = ne[i]){
			int j = e[i];
			cnt[j]--;
			if(cnt[j] == 0){
				q.push(j);
			}
		}
	}
	
	if(sum < n){
		return false;
	}else{
		return true;
	}
	
}

int main(){
	
	cin >> n >> m;
	
	memset(h, -1, sizeof(h));
	
	//建图
	for(int i = 1; i <= m; ++i){
		int a, b;
		cin >> a >> b;
		add(b, a);
		cnt[a]++;
	}
	
	if(top_sort()){
		cout << "Yes" << endl;
	}else{
		cout << "No" << endl;
	}
	
	
	return 0;
}

C、滁大七十周年校庆 — 救世主游戏

此题考查二维前缀和算法(不了解的同学可以自行百度),使用二维前缀和可以快速计算一个矩阵中任意一个子矩阵的矩阵和。如果直接无脑暴力计算,会因为时间复杂度过高而TLE(超时)。但是由于数据水了一点,导致大部分同学的暴力代码也AC(通过)了。

#include 

using namespace std;

const int N = 1e3 + 10;

int maze[N][N], s[N][N];
int n, m, k;

int main(){
	
	cin >> n >> m >> k;
	
	//读入原矩阵
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= m; ++j){
			cin >> maze[i][j];
		}
	}
	
	//计算二维前缀和 
	for(int i = 1; i <= n; ++i){
		for(int j = 1; j <= m; ++j){
			s[i][j] = s[i-1][j] + s[i][j-1] - s[i-1][j-1] + maze[i][j];
		}
	}
	
	int ans = 0;
	
	//枚举所有可能的子矩阵 维护最大值
	for(int i = k; i <= n; ++i){
		for(int j = k; j <= m; ++j){
			//使用二维前缀和快速计算子矩阵和
			int t = s[i][j] - s[i][j-k] - s[i-k][j] + s[i-k][j-k];
			//维护最大值
			ans = max(t, ans);
		}
	} 
	
	//输出结果
	cout << ans << endl;
	
	return 0;
}

D、滁大七十周年校庆 — 速算

读懂题意之后会发现,在已经确定中间数字Aj的情况下,只需要在Aj的前面选择一个最大的数字作为Ai,在Aj的后面选择一个最小的数字Ak,即可保证max( 0, ( Ai - Aj ) ) + max( 0, ( Aj - Ak ) ) 在选择Aj作为中间数的情况下保持最大。我们根据这个特征,可以预处理出每个位置前面的最大值数组和每个位置后面的最小值数组,然后枚举所有数字(除去两端)作为中间数字Aj的情况,再维护最大值即可。

#include

using namespace std;

const int N = 1e6 + 10;

int arr[N], ma[N], mi[N];
int t, n;

int main(){
    
    // 读入t
    cin >> t;
    
    //t组数据
    while(t--){
    	cin >> n;
    	//读入n个数字
    	for(int i = 1; i <= n; ++i){
    		cin >> arr[i];
		}
		
		//预处理最大值数组 ma[i]就代表第i个数字之前的最大值
		int temp = 0;
		for(int i = 1; i <= n; ++i){
			temp = max(temp, arr[i]);
			ma[i] = temp;
		} 
		
		//预处理最小值数组 mi[i]就代表第i个数字之后的最小值
		temp = 1001;
		for(int i = n; i >= 1; --i){
			temp = min(temp, arr[i]);
			mi[i] = temp;
		}
		
		//枚举所有情况 寻找最大值
		int ans = 0;
		for(int i = 2; i < n; ++i){
			ans = max(ans, max(0, ma[i-1] - arr[i]) + max(0, arr[i] - mi[i+1])); 
		} 
		
		//输出结果
		cout << ans << endl;
	}
	
	return 0;
}

滁大七十周年校庆 — 我也要

裸题,裸到不能再裸了,一看就知道是动态规划中的01背包模型。没什么好说的,直接套模板就行。这里展示优化成一维之后的01背包解法。

#include 

using namespace std;

const int N = 1010;

int t, m;
int dp[N];

int main(){
    
    cin >> t >> m;
    
    for(int i = 1; i <= m; ++i){
        int w, v;
        cin >> w >> v;
        for(int j = t; j >= w; --j){
            dp[j] = max(dp[j], dp[j-w] + v);
        }
    }
    
    cout << dp[t] << endl;
    
    return 0;
}

F、滁大七十周年校庆 — 寻路

直接BFS暴搜即可。走过的位置记得标记。C语言选手需要自己实现队列结构。

#include 
#include 

using namespace std;

const int N = 1e3 + 10;

typedef pair PII;

// 偏移量
int dir[4][2] = {
    {0, 1}, {1, 0}, {0, -1}, {-1, 0}};

string maze[N];
bool flag[N][N];
int n, m;

//是否出界
bool safe(int x, int y){
	if(x < 0 || x >= n || y < 0 || y >= m){
		return false;
	}
	return true;
}

int main(){
	
	cin >> n >> m;
	
	//读入地图 
	for(int i = 0; i < n; ++i){
		cin >> maze[i];
	}
	
	queue q;
	//放入起点 开始搜索
	q.push({n, 0});
	
	while(q.size()){
		PII t = q.front();
		q.pop();
		
		for(int i = 0; i < 4; ++i){
			int nx = t.first + dir[i][0];
			int ny = t.second + dir[i][1];
			if(safe(nx, ny) && !flag[nx][ny] && maze[nx][ny] == 'L'){
				//到达终点 
				if(nx == 0 && ny == m - 1){
					cout << "Yes" << endl;
					return 0;
				}else{
					q.push({nx, ny});
					flag[nx][ny] = true;
				}
			}
		}
		
	}
	
	//未搜索到通路
	cout << "No" << endl; 	
	
	return 0;
}

G、滁大七十周年校庆 — 循环相克令(番外篇)

本次比赛最水的题,也是签到题。几乎没有难度,读入字符串 + 分支判断即可。写法很多,此处展示一种供参考。

#include 

using namespace std;


int main(){
    
    int t;
    cin >> t;
    
    while(t--){
    	// C++中有现成的string类,底层也是char数组。C语言选手需要改用char数组来存储字符串。
        string op1, op2;
        cin >> op1 >> op2;
        
        if(op1 != op2){
            if(op1 == "Hunter"){
                if(op2 == "Gun"){
                    cout << "Player1" << endl;
                }else{
                    cout << "Player2" << endl;
                }
            }else if(op1 == "Bear"){
                if(op2 == "Hunter"){
                    cout << "Player1" << endl;
                }else{
                    cout << "Player2" << endl;
                }
            }else{
                if(op2 == "Bear"){
                    cout << "Player1" << endl;
                }else{
                    cout << "Player2" << endl;
                }
            }
        }else{
            cout << "Tie" << endl;
        }
    }
    
    return 0;
}

你可能感兴趣的:(ACM,acm竞赛)