2019山东省赛重现(部分题)

本文解析了2019年山东省算法竞赛中的六个题目,包括日历转换、迷宫机器人、图论游戏、桶中石子、区段令牌及游戏策略,涉及贪心算法、图论、数据结构和优先队列等核心概念。

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

**2019山东省赛重现 **
萌新又来写记录了
***A - Calandar ***
tips:每个月都只有30天,直接转化成天为单位算就可以~ 注意可以是之前的时间,也可以是之后的,开long long存~

#include<bits/stdc++.h>
#define ll long long
using namespace std;

string book[] = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
ll t,year,mon,day,a,b,c,now;
string s;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%lld%lld%lld",&year,&mon,&day);
		cin >> s;
		scanf("%lld%lld%lld",&a,&b,&c);
		ll buff = ((a - year) * 360 + (b - mon) * 30 + (c - day)) % 5;
		if(buff < 0)	buff += 5;
		for(int i = 0; i < 5; i++)		//这里也可以用map存,然后直接查询
            if(book[i] == s)
                now = i;
		cout << book[(now + buff) % 5] << endl;
	}
	return 0;
} 

**C - Wandering Robot ***
tips:不是算完一次然后
k就可以!应该是找到第k-1次的终点,然后从这个终点再跑最后一次~(取得的最大值只可能在第一次或者最后一次)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll ans,n,t,k,len,x,y,endx,endy,maxx,maxy;
string command;

void solve(int i){
	if(command[i] == 'U')	y += 1;
	if(command[i] == 'D')	y -= 1;
	if(command[i] == 'R')	x += 1;
	if(command[i] == 'L')	x -= 1;
}
int main(){
	scanf("%d",&t);
	while(t--){
		ans = x = y = 0;
		scanf("%d%d",&n,&k);
		cin >> command;
		len = command.length();
		for(int i = 0; i < len; ++i){
			solve(i);
			ans = max(ans, abs(x) + abs(y));
		}
		endx = x * (k - 1), endy = y * (k - 1);
		/*printf("endx = %d endy = %d \n",endx,endy);
		ans = max(ans, abs(endx) + abs(endy));*/
		x = endx, y = endy;
		for(int i = 0; i < len; ++i){
			solve(i);
			ans = max(ans, abs(x) + abs(y));
		}
		printf("%lld\n",ans);
	}
	return 0;
}

***D - Game on a Graph ***
tips:图论(离散数学)的应用。一个无向简单连同图,最少要有n-1条边。所以第m-(n-1)条边是谁选,该队伍就输~(注意要mod k,因为是循环选的,即队伍的最后一个人选完,接着轮到第一个人选)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int t,k,book[100005],n,m,u,v;
int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&k);
		for(int i = 0; i < k; ++i)
			scanf("%1d",&book[i]);
		scanf("%d%d",&n,&m);
		for(int i = 1; i <= m; ++i)
			scanf("%d%d",&u,&v);
		int temp = (m - (n - 1)) % k;
		book[temp] == 1 ? printf("2\n") :  printf("1\n");
	}
	return 0;
}

***F - Stones in the Bucket ***
tips:贪心。两个策略分别为:A.每次拿出一块石头 B.每次拿出一块石头放入另一个
可以发现,最后的状态是ave = sum / n;
对于等于ave的桶,不作处理;
对于小于ave的桶,只能由大于ave的桶采取B策略,所以可以暂时忽略,等到大于ave的桶时在计算;
对于大于ave的桶,ans += book[i] - ave;(要么是给小于ave的桶(B策略);要么是A策略直接扔掉)

#include<bits/stdc++.h>
#define ll long long
using namespace std;

ll t,book[100005],sum,ave,ans,n;

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%lld",&n);
		sum = ans = 0;
		for(int i = 1; i <= n; ++i){
			scanf("%lld",&book[i]);
			sum += book[i];
		}
		ave = sum / n;
		if(!ave)	printf("%lld\n",sum);	
		else{
			for(int i = 1; i <= n; ++i){
				if(book[i] < ave || book[i] == ave)	continue;
				else if(book[i] > ave)
					ans += book[i] - ave;
			}
			printf("%lld\n",ans);
		}
	}
	return 0;
}

***H - Tokens on the Segments ***
tips:贪心+优先队列!
这个题还是很好玩的,值得整理一下~
线段短的应该优先考虑(因为长的可选择的点(机会)多,短的(例如只有一个点)机会少,应该先选。按照左端点排序会有两种情况:A.某一条线段的左端点是唯一的,只需要选择这个点,就可以选择这个线段,而不影响其他线段;B.不唯一,按照线段长短来选择)
这种线段+区间的问题,一般都会 按左端点小到大排序,左端点相同则右端点小到大排序~(不绝对,但是这是一种可以考虑的思路,反过来也行)。这样排序在左端点相同的情况下,按照线段短->长的顺序考虑。结构体中重载小于号,然后用优先队列维护线段。last记录上一次选取的点的x坐标。
若l > last,则cnt++, last = l;
否则如果last + 1 <= r(<号不可以去掉~)把l更改成last + 1再放入优先队列中等待下一次计算!(而不是直接选这条线段的第last + 1个点,如下图情况)

一种情况

#include<bits/stdc++.h>
#include<queue> 
#define ll long long
using namespace std;

struct node{
	int l,r;
	bool operator < (const node& rhs) const{
		if(rhs.l != l)	return l > rhs.l;
		else	return r > rhs.r;
	}
	node(int lll, int rr){l = lll, r = rr;} 
	node(){}; 
}book[100005];
int t,n,last,cnt;

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d",&n);
		priority_queue<node> q;
		for(int i = 1; i <= n; ++i){
			scanf("%d%d",&book[i].l,&book[i].r);
			q.push(book[i]); 
		}
		last = cnt = 0;
		while(!q.empty()){
			node n1 = q.top(); q.pop();
			if(last < n1.l)	last = n1.l, cnt++;
			else{
				if(last + 1 <= n1.r)
				q.push(node(last + 1, n1.r)); 
			}
		}
		printf("%d\n",cnt);
	}
	return 0;
}

**M - Sekiro ***
tips:直接算肯定会超时。写一写就会发现,k每增加1,n就会
2,是2的指数关系。因为n不超过10的9次方,大约2的31次方就已经超过这个值了,直接给答案1就可以~ 注意n/2要转化成double,否则5/2==2~

#include<bits/stdc++.h>
#define ll long long
using namespace std;

int t,n,k;

int main(){
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&k);
		if(!n)	printf("0\n");
		else if(k >= 50)	printf("1\n");		//可以再小一点,随便啦
		else{
			for(int i = 1; i <= k; ++i){
				n = ceil((double)n / 2);
				if(n == 1)	break;
			}
			printf("%d\n",n);	
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值