QLU_凉脾的比赛题解

之前都是只补的没有做出来的题目的题解,但是这次想着也把做出来的题目的题解也放上来,索然题目都不难,也算是记录一下自己的学习历程吧 QAQ

A - DRM Messages

题目大意:

把一个输入的字符串进行解密处理,处理的步骤:

一:划分,把字符串从中间分为两段。

二:旋转,对于每一半,通过将每个字符的值相加(A=0,B=1,…,Z=25)来计算其旋转值。然后将这一半字符串上的每一 个字符移动其旋转值次,必要时从Z换行到A。其实说白了就是一个等价问题。先计算指定的值,然后寻找每个字符的对于该指定 值的等价字符。左右两边的处理方式相同。

三、合并,通过将左边字符串中的每个字符旋转右边字符串中相应字符的值来组合这些新字符串。也就是左边的字符串中的每个字符加上对应位置的右边字符串的值。

输入:

输入一个字符串。字符串中的所有字符均为大写字母,字符串长度为偶数且小于等于15000。

输出:

解密后的字符串

解题思路:

直接按照题意模拟即可,可能中间的处理过程有些技巧性的东东,所以多做一些题目还是有很大好处的,可以学习其他人代码的优越的部分 ^ _ ^

#include<bits/stdc++.h>
using namespace std;
const int maxn=15000+10;
string s;   //加密字符串
int a[maxn];    //每个字符对应的值
struct node
{
  char z;
  int x;    //对应字母的值
}c[maxn];
int sumr;    //左半部分字符串的旋转值
int suml;   //右半部分字符串的旋转值
int main()
{
  cin>>s;
  int len=s.length();
  for(int i=0;i<26;i++)   //为结构体数组分配字符和对应的值 
  {
    c[i].z='A'+i;
    c[i].x=i;
  }
  for(int i=0;i<len;i++)    //每个字符对应的值
   for(int j=0;j<26;j++)
   {
     if(s[i]==c[j].z)
     {
       a[i]=c[j].x;    
     }
   }
  for(int i=0;i<len/2;i++)   //计算左半部分的值
  {
    for(int j=0;j<26;j++)
     if(s[i]==c[j].z)
     {
       sumr+=c[j].x;
       break;
     }
  }
  for(int i=len/2;i<len;i++)   //计算右半部分的值
  {
    for(int j=0;j<26;j++)
     if(s[i]==c[j].z)
     {
       suml+=c[j].x;
       break;
     }
  }
  sumr=sumr%26;    //可能计算得到的值会反一轮或者几轮,这样处理可以避免重复处理,简化计算。
  suml=suml%26;
  for(int i=0;i<len/2;i++)     //左半部分进行旋转处理
  {
    a[i]+=sumr;
    if(a[i]>=26)             //换行处理
     a[i]-=26;
  }
  for(int i=len/2;i<len;i++)   //右半部分进行旋转处理
  {
    a[i]+=suml;
    if(a[i]>=26)              //换行处理
     a[i]-=26;
  }
  for(int i=0,j=len/2;i<len/2,j<len;i++,j++)    //合并操作
  {
    a[i]=a[i]+a[j];   
    if(a[i]>25)
     a[i]%=26;
  }
  for(int i=0;i<len/2;i++)
   a[i]+='A';
  for(int i=0;i<len/2;i++)
   cout<<char(a[i]);
  cout<<endl;

  return 0;
}

可以看出来前面有相当一部分是可以简化的,有更简单的方式去完成相同的处理。所以:

#include<bits/stdc++.h>
using namespace std;
const int maxn=15000+10;
string s;   //加密字符串
int a[maxn];    //每个字符对应的值
int sumr;    //左半部分字符串的旋转值
int suml;   //右半部分字符串的旋转值
int main()
{
  cin>>s;
  int len=s.length();
  for(int i=0;i<len;i++)    //直接将每个字符对应的值计算出来
   a[i]=s[i]-'A';
  for(int i=0;i<len/2;i++)   //计算左半部分的值
  {
    for(int j=0;j<26;j++)
     if(s[i]==c[j].z)
     {
       sumr+=c[j].x;
       break;
     }
  }
  for(int i=len/2;i<len;i++)   //计算右半部分的值
  {
    for(int j=0;j<26;j++)
     if(s[i]==c[j].z)
     {
       suml+=c[j].x;
       break;
     }
  }
  sumr=sumr%26;    //可能计算得到的值会反一轮或者几轮,这样处理可以避免重复处理,简化计算。
  suml=suml%26;
  for(int i=0;i<len/2;i++)     //左半部分进行旋转处理
  {
    a[i]+=sumr;
    if(a[i]>=26)             //换行处理
     a[i]-=26;
  }
  for(int i=len/2;i<len;i++)   //右半部分进行旋转处理
  {
    a[i]+=suml;
    if(a[i]>=26)              //换行处理
     a[i]-=26;
  }
  for(int i=0,j=len/2;i<len/2,j<len;i++,j++)    //合并操作
  {
    a[i]=a[i]+a[j];   
    if(a[i]>25)
     a[i]%=26;
  }
  for(int i=0;i<len/2;i++)
   a[i]+='A';
  for(int i=0;i<len/2;i++)
   cout<<char(a[i]);
  cout<<endl;

  return 0;
}

B - Game of Throwns

题目大意:

有一个由 n 个人,从 0n-1 顺时针围成的圈和一个鸡蛋,鸡蛋总是从 0 开始滚动。给定一串命令,正数代表顺时针滚动,否则就逆时针滚动。此外,还可以撤销命令:undo t,代表撤销前面的t条命令。问最后鸡蛋在哪个位置。

解题思路:

开一个数组,存储每次操作后鸡蛋在哪个位置,当命令出现undo时,数组的位置就向前推undo要求的步数。此时还应该注意,可能出现负数,在此:
当要求a模b,需分情况讨论:

  • 当a>=0 : a%b
  • 当-b<=a<0 : (a+b)%b
  • 当a<-b : (a%b+b)%b

代码:

#include <bits/stdc++.h>
using namespace std;
char s[20];
int cmd[105];
int main()
{
	int n,m;
	int cur;
	scanf("%d%d",&n,&m);
	cur=0;
	for(int i=0;i<m;i++)
	{
		scanf("%s",s);
		if(isdigit(s[0])||s[0]=='-') 
		    cmd[cur++]=stoi(string(s));
		else
		{
			int undo;
			scanf("%d",&undo);
			cur-=undo;
		}
	}
	int ans=0;
	for(int i=0;i<cur;i++) 
	  ans=(ans+cmd[i]%n+n)%n;
	printf("%d",ans);
	return 0;
}

也从这个题解里面学习到了新的函数,操作更方便,isdigit()函数和stoi()函数 。——>>stoi()函数学习记录

C - Sheba’s Amoebas

题目大意:

有一个m行n列由 #. 组成的矩阵,问里面有几个互不连通的圈。

解题思路:

从矩阵开头开始查找 #,并开一个数组记录该位置是否被访问过,没有被访问的话再以这个点为基点向八个方向继续查找,同时为每个查找的点分配一个值,每一个圈的第一个 # 记录为1,当最终回到这个点时,sum++。遍历完整个矩阵时,输出sum.

代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=100+10;
int dx[] = {0,0,-1,-1,-1,1,1,1};
int dy[] = {1,-1,0,1,-1,0,-1,1};
int sum;
int vis[maxn][maxn];
char s[maxn][maxn];
int a[maxn][maxn];
int n,m;  //m行n列
int tmp;
void dfs(int x,int y)
{
  a[x][y]=++tmp;
  // cout<<x<<" "<<y<<endl;
  for(int i=0;i<8;i++)
  {
    int xx=x+dx[i];
    int yy=y+dy[i];
    if(a[xx][yy]==1)
    {
      sum+=1;
      // cout<<"sum="<<sum<<endl;
      // cout<<"YES"<<endl;
    }
    if(xx>=1&&xx<=m&&yy>=1&&yy<=n&&!vis[xx][yy]&&s[xx][yy]=='#')
    {
      vis[xx][yy]=1;
      dfs(xx,yy);
    }
  }
}
int main()
{
  cin>>m>>n;
  for(int i=1;i<=m;i++)
   for(int j=1;j<=n;j++)
    cin>>s[i][j];
  for(int i=1;i<=m;i++)
   for(int j=1;j<=n;j++)
   {
     tmp=0;
     if(s[i][j]=='#'&&!vis[i][j])
     {
       dfs(i,j);
     }
   }
  cout<<sum<<endl;

  return 0;
}

E - A Question of Ingestion

dp问题还是不大会 QAQ

题目大意:

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

解题思路:

dp[ i ][ j ]来记录在第 i 天,连续摄入第 j 天时能得到的总量的最大值。dp[ i ][ j ]的值要么是前一天选择吃饭转移来的,或者是前一天没有吃饭转移过来的。需要注意的是dp[ i ][ 1 ]可能是大前天转移过来的,这表示中间的两天选择不吃饭。

每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 n,m;
int a;
int main()
{
	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++)
		{
			if(dp[i][j]<0) 
			  continue;
			int aftereat=dp[i][j]+min(e[j],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);
		}
	}
	int ans=0;
	for(int j=0;j<=n;j++) 
	  ans=max(ans,dp[n][j]);
	printf("%d",ans);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值