原题网址:
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.不大懂得过程试着暂停输出一下。