寒假·凉脾的比赛 题解

原题网址:
https://vjudge.net/contest/352642
题解网址:
https://blog.csdn.net/kangyupl/article/details/104034049

A.DRM Messages

题意:

模拟DRM加密的三个步骤:
第一步:划分。把字符串从中间分成两段。
第二步:旋转。算出左字串每个字符相对于A的偏移值之和,然后每个字符加长这个和值。再对右字串做同样处理。
第三步:合并。算出处理后的右字串每个字符相对于A的偏移值,每个右字串的字符所对应的左字串的字符都加上对应的偏移值。

思路:

咋说咋做。

include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
char a[25005];
int main() {
	int i,n,sum1=0,sum2=0,c1,c2,c,b;
	gets(a);
	n=strlen(a);
	for(i=0; i<n/2; i++)
		sum1+=a[i];
	c=i;
	for(i=n/2; i<n; i++)
		sum2+=a[i];
	sum1-=c*65;
	sum2-=c*65;
	c1=sum1%26;
	c2=sum2%26;
	for(i=0; i<n/2; i++) {
		a[i]+=c1;
		if(a[i]>'Z') a[i]-=26;
	}
	for(i=n/2; i<n; i++) {
		a[i]+=c2;
		if(a[i]>'Z') a[i]-=26;
	}
	for(i=0; i<n/2; i++) {

		a[i]+=a[i+n/2]-65;
		if(a[i]>'Z') a[i]-=26;

	}
	for(i=0; i<n/2; i++) 
		printf("%c",a[i]);
	return 0;
}

B.Game of Throwns

题意:

总共有n个人,k条命令。人从0开始编号,开始的时候0号拿一个蛋蛋。
命令分两种:
· 一个数字t,表示把蛋蛋顺时针传t次,t可能小于0
· 一个“undo”接一个数字t,表示抵消最后t条命令。抵消的时候跳过其他undo。

思路:

另:

此题意在教大家负数取模的,具体做法看代码
总结: 当要求a模b,最终结果取自然数时,需分情况讨论:
当a>=0 : a%b
当-b<=a<0 : (a+b)%b
当a<-b : (a%b+b)%b

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[100];
int main() {
	cin>>n>>k;
	int c=0;
	for(i=0;; i++) {
		gets(a);
		if(isdigit(a[0])||a[0]=='-') b[c++]=stoi(string(a));
		else {
			int undo;
			cin>>undo;
	 		c=max(c-undo,0);
		}
	}
	int ans=0;
	for(i=0;i<c;i++) ans+=b[i];
	//for(int i=0;i<cur;i++) ans=(ans+c[i]%n+n)%n;
    /*
    易错点:负数取模
    cmd[i]本身可能小于-ans-n,加ans后会小于-n,故先对它取模
    答案要求结果为0~n-1,但(ans+cmd[i]%n)%n可能小于0,故要先加上n再%n
    */
	printf("%d",ans);
	return 0;
}

C.Sheba’s Amoebas

题意:

给定一个黑白矩阵,规定每个格子与上下左右和斜对角的格子相互连通。求里面不相连通的黑块儿有几个。

思路:

涉及dfs,从0开始的学习,这边稍微写一下其原理,以后再统的说一下:
两个数组,一个用来标记该点是否被访问过,一个用来把该点放入数组,所以这两个标记是相辅相成的,一定同时出现;dfs就是随机选定一个起点将其标记为已经访问过的点,然后就是递归调用进行与其相邻的点的搜索,直到所有的点都被访问完。

此题则是DFS每个格子。单次DFS到的格子编上相同的编号,每次DFS完后若本次访问的黑格子数>0,就把总编号数+1。
这题拿来练dfs很经典。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
char a[105][105],b[105][105];
int m,n;
int c,s;
void dfs(int y,int x) {
	if(x<0||y<0||x>=n||y>=m||a[y][x]!='#'||b[y][x]>0) return;
	//最后那一个>0可能不大好理解。意为这个数字已经在之前统计过了,
	//要么是同一圈要是前边几圈的,不然一定为0(未标记状态)
	b[y][x]=s;//前边筛选已过,现在标记,形象点说每次标记的符号(颜色)不一样;
	c++;//作用是来证明存在有草履虫循环
	int oy,ox;
	for(oy=-1; oy<=1; oy++)
		for(ox=-1; ox<=1; ox++)
			dfs(y+oy,x+ox);//九宫格走一遍
}
int main() {
	int i,j;
	cin>>m>>n;
	for(i=0; i<m; i++) {
		scanf("%s",a[i]);
		memset(b[i],0,sizeof(int)*n);
		//第一个二维数组存数,第二个二维数组标记,
		//这里memset一行一行的清零
	}
	s=1;//用1来标记,否则可能和空白标记重合(大概?
	for(i=0;i<m;i++){
		for(j=0;j<n;j++){
			c=0;//每次清零来证明存在性
			dfs(i,j);
			if(sum>0) s++;//存在即加一,标记颜色变更
		}
	}
	cout<<s-1<<endl;//把留出来的那次标记颜色去掉即为最终标记颜色种类
	return 0;
}

D.Keeping On Track

大半夜题解看的有点懵,看不大懂,稍微有点超出能力,暂留。

E. A Question of Ingestion

彻头彻尾的dp题。从这题开始学习dp。
题意:

有最多100天。每天有一个食物量,你一开始有一个最大胃口表示你最开始能吃多少食物。
如果你昨天吃了,那么今天的胃口为昨天的2/3。如果你前天吃了,昨天没吃,那么你的胃口可以恢复到前天的情况。
如果你有连续两天没吃了,那么你就可以恢复到最大胃口了。给定每天不同的食物量,问怎样安排使一共吃到的食物最多?

思路:

先根据题意,每x天的饭量取值只能为m*[(2/3) ^ x],x取值为0,1,2…n,所以我们可以先把每天饭量的可能取值求出来,存进数组e里。
然后定义dp[i][j]表示第i天,饭量为e[j]时,吃掉的食物总量。
先把初始值设为-1,表示未赋值。然后让dp[i][0]全等于0,表示一直不吃的情况。
然后分类讨论:

今天吃完后的分值为dp[i][j]+min(e[j],a),记为aftereat
今天吃,明天也吃:dp[i+1][j+1]=max(dp[i+1][j+1],aftereat)
今天吃,明天不吃,后天吃:dp[i+2][j]=max(dp[i+2][j],aftereat)
今天吃,明天不吃,后天也不吃,大后天吃:dp[i+3][0]=max(dp[i+3][0],aftereat)

这里先放一份把大佬的题解进行加工的,以便日后理解的版本

#include <bits/stdc++.h>
using namespace std;
int dp[105][105];
int e[105];
int main() {
	int n,m,a;
	scanf("%d%d",&n,&m);
	memset(dp,-1,sizeof(dp));//表示未赋值
	for(int i=0; i<n; i++) dp[i][0]=0; //可以运用的数组,起始位设立 
	e[0]=m;//起始饭量
	for(int i=1; i<=n; i++) e[i]=e[i-1]*2/3; //饭量储存
	for(int i=0; i<n; i++) {
		scanf("%d",&a);//输入每小时提供的饭
		for(int j=0; j<=n; j++) { //i代表时间,j代表累计次数
		//不大好理解的就是那个j=0,j<=n,这玩意就让人费解,上边的e数组的范围和这个一样,我暂且理解为必须比原定范围左右大一点才能实现预估路线的dp大法 
			if(dp[i][j]<0) continue;//前几次循环中dp没有跑到的地方就不要了
			int aftereat=dp[i][j]+min(e[j],a);
			printf("<<aftereat=dp[%d][%d]+a=%d+%d=%d>>\n",i,j,dp[i][j],a,aftereat);// 现在,今天,这一天吃完的分值,从原可能分支基础上开始加的 ,a为提供的饭,即为前几天的合
			dp[i+1][j+1]=max(dp[i+1][j+1],aftereat); //连续吃的第二天
			dp[i+2][j]=max(dp[i+2][j],aftereat);//隔一天保持一样
			dp[i+3][0]=max(dp[i+3][0],aftereat);
			printf("#dp[%d][%d]=%d,dp[%d][%d]=%d,dp[%d][%d]=%d#\n\n",i+1,j+1,dp[i+1][j+1],i+2,j,dp[i+2][j],i+3,0,dp[i+3][0]);//printf("%d  %d  %d\n",dp[i+1][j+1],dp[i+2][j],dp[i+3][0]);//隔两天饭量恢复,j重新为0
		}//跟不同选择不同走向的rpg一样,每次延伸出来可能分支,最后的分支树必定大于你结局的位置,但我们答案就卡死在结局所在的层面进行最大选择 
	}
	int ans=0;
	for(int j=0; j<=n; j++) {
		ans=max(ans,dp[n][j]);
		printf("dp[%d][%d]=%d  ",n,j,dp[n][j]);
	}
	printf("\n\n***%d***",ans);

	return 0;
}

随后放上一份自己照着思路打的一份。以后看上边那份。

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<stdlib.h>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int  dp[105][105];
int  e[105];
int main() {
	int n,m,i,a,j;
    cin>>n>>m;
    memset(dp,-1,sizeof(dp));
    for(i=0;i<n;i++) dp[i][0]=0;
    e[0]=m;
	for(i=1;i<=n;i++) e[i]=e[i-1]*2/3;
	for(i=0;i<n;i++){
		cin>>a;
		for(j=0;j<=n;j++){
			if(dp[i][j]<0) continue;
			int today=dp[i][j]+min(e[j],a);
			dp[i+1][j+1]=max(dp[i+1][j+1],today);
			dp[i+2][j]=max(dp[i+2][j],today);
			dp[i+3][0]=max(dp[i+3][0],today);
		}
	}
	int sum=0;
	for(i=0;i<=n;i++)  sum=max(sum,dp[n][i]);
	cout<<sum<<endl;
	return 0;
}

F.Is-A? Has-A? Who Knowz-A?

唉看了一眼题解直接战略放弃。不耽误时间了。

总结:
1.抓住典型题学习算法
2.不大懂得过程试着暂停输出一下。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值