2023年广东省大学生程序设计竞赛题解

A: 算法竞赛


解题思路:

​ 签到题,就不说了。

解题代码:

#define int long long
const int N = 1e6+5;
int arr[N];

void solve() {
	int n,m; cin >> n;
	int len = 0; cin >> len;
	for(int i = 1; i<= len; i++)
		cin >> arr[i];

	cin >> m;
	int ans = 0, l = 1;
    
	for(int i = n; i <= m; i++){
		if(arr[l] == i && l <= len){
			l ++;
			continue;
		}
		ans ++;
	}
	cout << ans << endl;
}

B: 基站建设

解题思路:
该题是一道动态规划(dp)的题,还需要进行滑动窗口的优化才能过。
该题的主要思路如下:

  1. 先对这个进行线段进行处理,由于需要对任意两个线段之间安装站点,那么可以进行预处理,将右端的点指向左端点。
  2. 然后进行dp的操作,因为只需要对当前区间上一个需要安装站点的区间进行操作,如果不在上一个节点之内的值全进行pop掉,然后取上一个区间的最小值。
  3. 因为要取区间的最小值,可以用优先队列(滑动窗口)算法进行操作,就可以进行得到需要的值。

解题代码:

const int N = 1e6 + 6;
array<int,N> arr;
int dp[N];    // dp值
int pre[N];   // 
void solve(){
	int n,m; cin >> n;
	for(int i = 1; i<= n; i++){
		cin >> arr[i];
		dp[i] = 1e18;  // 初始化
	}
	
	for(int i =1 ; i <= n + 1; i++) pre[i] = 0;   // 初始化
	cin >> m;
	for(int i= 1; i <= m; i++){
		int a, b ; cin >> a >> b;
		pre[b + 1] = max(pre[b + 1], a);   // 进行线段的处理
	}
	
	priority_queue<PII,vector<PII>, greater<PII> > pq;  // 定义小根堆
	pq.push({0,0});
	
	for(int i = 1; i <= n; i++){
		// 滑动窗口
		while(pq.size() && pq.top().se < pre[i]) pq.pop();    
		dp[i] = pq.top().fi + arr[i];  // dp操作
		pq.push({dp[i],i}); 
	}
	while(pq.size() && pre[n + 1] > pq.top().se) pq.pop();     // 为了使最后的得到的值在在最后一个区间,比如如果最后一段的右节点为n,那么就没法保证区间在最后一段
	cout << pq.top().fi << endl;
	return ;
}

C: 市场交易

思路:

该题就是双指针,注意数据范围。

代码:

#define PII pair<int,int>
#define int long long 
using namespace std;
 
const int N = 1e6+5;
PII arr[N];
 
void solve() {
	int n,m;
	cin >> n;
	for(int i = 1; i<= n; i++)
		cin >> arr[i].fi >> arr[i].se;
	
	sort(arr + 1,arr + 1+ n);
	int l = 1, r = n;
	int ans = 0;
	while(r > l){
		int maxx = min(arr[l].se, arr[r].se);
		int res = maxx * (arr[r].fi - arr[l].fi);
		ans += res;
		arr[l].se -= maxx, arr[r].se -= maxx;
		if(arr[l].se == 0) l ++;
		if(arr[r].se == 0) r --;
	}
	cout << ans << endl;
	
}

D: 新居规划

思路:

​ 该题运用前缀和和遍历的思想进行实现的。

​ 首先先对其排个序(排序方式就是求 a i − b i a_i - b_i aibi,大的排前面,小的排后面), 因为会有两段,一段是有领居的,一段是没有领居的。因为有领居和没有领居只能取一边,那么就形成的这样的一个构造:

84db4908b9b62770ca0ea41d6e829c6

那么就只需要进行枚举就行了,枚举没有领居的个数,然后判断该没有领居的个数是否合法。如果合法的话,就进行计算这样形成的情况得到的满意度的总值,然后再进行比较哪个总值最大。

注意:当有领居的个数为1和0得到的值都是一样的,这个需要进行特判。

代码:

#define int long long 
const int N = 1e6+5;
PII arr[N];

bool cmp(PII a, PII b){
	return a.fi - a.se > b.fi - b.se;
}
int suma[N],sumb[N];

void solve() {
	int n,m;
	cin >> n >> m;
	for(int i = 1; i<= n; i++){
		cin >> arr[i].fi >> arr[i].se;
		suma[i] = 0;
		sumb[i] = 0;
	}
	sort(arr + 1, arr + 1+ n, cmp);
	
	for(int i= 1; i <= n; i++){
		suma[i] = suma[i - 1] + arr[i].fi;
		sumb[i] = sumb[i - 1] + arr[i].se;
	}
	int ans = 0;
	for(int i = 0; i <= n; i++){
		int j = m - 2 * i;
		if(j < n - i) {
			break;
		}
		int res = 0;
		if(i >= n - 1) res = sumb[n];
		else res = suma[n - i] + sumb[n] - sumb[n - i];
		ans = max(ans,res);
	}
	cout << ans << endl;
}

I: 路径规划

解题思路:

​ 该题的主要思路是二分。二分答案,这个主要找到先后关系,就比如在同一条线上,他们的位置大小顺序肯定都是依次递增的,那么我们就可以进行二分答案。该题主要就是check函数,如果直接在check函数中排序,那么时间会在第6个样例超时,那么就需要进行优化。

​ 既然不能在check函数中进行排序,那么就在main函数中进行排序,然后在check函数中进行遍历的时候,因为坐标是排好序的状态,当遍历到小于等于x的时候就进行比较与前面的那个的大小,如果出现不是递增的状态话就说明不成立。

解题代码:

bool cmp(PII a, PII b){
	if(a.se.fi != b.se.fi)  return a.se.fi > b.se.fi;
	return a.se.se > b.se.se;
}

vector<pair<int,pair<int,int>>> temp;

bool check(int x){
	int a,b,flag = 0;
	for(int i= 0; i < temp.size();i++)
		if(flag == 0 && temp[i].fi <= x){
			flag = 1;
			a = temp[i].se.fi, b = temp[i].se.se;
		}
		else if(temp[i].fi <= x){
			if(temp[i].se.fi > a || temp[i].se.se > b)
				return false;
			
			a = temp[i].se.fi, b = temp[i].se.se;
		}

	return true;
}

void solve() {
	int n,m;
	cin >> n >> m;
	temp.clear();
	for(int i = 1; i<= n; i++){
		for(int j = 1; j <= m; j++){
			int a; cin >> a;
			temp.push_back({a,{i,j}});
		}
	}
	sort(temp.begin(),temp.end(),cmp);
	
	int l = 1, r = n*m;
	while(r > l){
		int mid = l + r >> 1;
		if(check(mid)) l = mid + 1;
		else r = mid;
	}
	cout << r << endl;
	
}

K: 独立钻石

解题思路:

​ 看到这个题的数据范围就是大暴搜,直接搜索每个位置就行了。

解题代码:

const int N = 10;
int arr[N][N];
int xx[] = {0,0,1,-1},yy[] = {1,-1,0,0};
int n,m,k;
int ans = 1e9;

void dfs(){
//	cout << 111 << endl;
	for(int i = 1; i <= n; i++)
		for(int j = 1; j <= m; j++)
			if(arr[i][j] == 1)
				for(int k = 0; k < 4; k++){
					int tempx = i + xx[k], tempy = j + yy[k];
					if(tempx > 0 && tempx <= n && tempy > 0 && tempy <= m && arr[tempx][tempy] == 1){
						int tempxx = tempx + xx[k],tempyy = tempy + yy[k];
						if(tempxx > 0 && tempxx <= n && tempyy > 0 && tempyy <= m && arr[tempxx][tempyy] == 0){
							arr[tempxx][tempyy] = 1;
							arr[tempx][tempy] = 0;
							arr[i][j] = 0;
							dfs();
							arr[tempxx][tempyy] = 0;
							arr[tempx][tempy] = 1;
							arr[i][j] = 1;
						}
					} 
				}
	int res = 0;
	for(int i =1 ; i <= n; i++)
		for(int j = 1; j <= m; j++)
			if(arr[i][j] == 1)
				res ++;
	ans = min(ans,res);
}

void solve() {
	cin >> n >> m >> k;
	memset(arr,0,sizeof(arr));
	ans = 1e9;
	for(int i = 1; i<= k; i++){
		int a,b; cin >> a >> b;
		arr[a][b] = 1;
	}
	dfs();
	cout << ans << endl;
}

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值