A. Array with Odd Sum


题意

给出包含 n 个正整数的序列 a ,你可以把任何一个元素 $a_i$ ,赋值给另一个元素 $a_j$ ($i\neq j$) ,问通过任意此操作能否将序列 a 的和变为奇数。可以输出 YES ,不可以输入 NO.


思路

首先当起始和为奇数的时候,就直接可输出 YES 了,如果是偶数的话,我们可以发现,如果序列元素中同时包含奇数和偶数,那么就是可以的,否则不可以。


代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

int t,n,flag,sum,p,flag1,flag2;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		flag=false;
		flag2=flag1=false;
		sum=0;
		scanf("%d",&n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&p);
			sum+=p;
			if(p%2==1) flag1=true;
			if(p%2==0) flag2=true;
		}
		if(flag1&&flag2) flag=true;
		if(sum%2==1) printf("YES\n");
		else 
		{
			if(flag) printf("YES\n");
			else printf("NO\n");
		}
	}
}

B. Food Buying


题意

初始有 s 个货币,每次花费 x 个货币会返还 $\lfloor{\frac{x}{10}}\rfloor$ 个货币,问最多共能花费多少货币。


思路

贪心即可。剩余的货币一直除10累加,注意最终剩余不足10的处理。


代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>

using namespace std;

int t,s;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&s);
		{
			int p=s;
			int now=0;
			while(p)
			{
				if(p<10) break;
				now=p/10;
				s+=now;
				p%=10;
				p+=now;
			}
		}
		printf("%d\n",s);
	}
}

C. Yet Another Walking Robot


题意

一个机器人初始在 $(0,0)$ 点,规定 ‘L’‘R’‘U’‘D’ 分别对应向左,向右,向上和向下。给定一段包含上述字母的序列 s ,机器人遵循指引序列移动。如果删除一段连续序列可使得机器人最终到达终点不变,问删除的最短序列的起始和终点为多少。


思路

想了半天想了错误的解法。。一直在考虑 LR 数相等,UD 相等,通过这个方法来找序列。看了题解才发现是通过坐标来看。我们可以开一个map记录坐标和步数的关系,从左到右扫序列,如果没有到达过这个坐标,就记录当前是第几次移动到达这个坐标的,如果到达过的话,就看上一次到达这个坐标时的步数,计算他们的序列长度,如果小于计算的就更新答案。因为是需要找最小的,因此只需要记录上一次到达的步数即可。


代码实现


#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>

using namespace std;

const int maxn=200005;
int t,n;
char s[maxn];
bool flag;
int main()
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d",&n);
		int l=-1,r=n;
		scanf("%s",s+1);
		pair<int,int> pos; //first为x second为y
		map<pair<int,int>,int> last;  
		pos.first=pos.second=0;
		last[pos]=0;
		for(int i=1;i<=n;i++)
		{
			if(s[i]=='L') pos.first--;
			if(s[i]=='R') pos.first++;
			if(s[i]=='U') pos.second++;
			if(s[i]=='D') pos.second--;
/*			
			if(i==2)
			{
				printf("%d %d\n",pos.first,pos.second);
			}
*/			
			if(last.count(pos)!=0)
			{
				int p=i-last[pos];
//				if(i==2) printf("%d %d\n",i,last[pos]);
				if(p<r-l)
				{
					l=last[pos];
					r=i;	
				}	
//				if(i==2 )printf("%d %d\n",l,r);
			}
			last[pos]=i;
		}
		if(l==-1)
		{
			printf("-1\n");
		}
		else
		{
			printf("%d %d\n",l+1,r);
		}
	}
	return 0;
}

D. Fight with Monsters


题意

由你先手和对手轮流击打 $n$ 个血量为 $h_i$ 的小怪兽,你可以对怪物造成 $a$ 点伤害,对手可以造成 $b$ 点伤害。你有 $k$ 次机会使对手跳过他的回合。当小怪兽血量 $h\le0$ 时视为被击杀,当你击杀怪兽,你获得一分,当对手击杀,你不得分。求你最多能获得多少分数。


思路

先看一下对于每个怪兽我们要击杀需要花费多少机会,你和对手一个回合会击杀怪兽 $a+b$ 点血量,因此你可以一直将回合进行到怪兽血量小于$a+b$,接下来我们可以分两种情况讨论。

  • 怪兽血量为0,那么我们就需要回溯对手最后一个回合,然后需要使用的机会就是 $\lceil\frac{h_i}{a}\rceil$ 次

  • 怪兽血量不为0,我们需要使用的机会就是 $\lceil\frac{h_i}{a}\rceil-1$ 次,注意这里不能直接写 $\lfloor\frac{h_i}{a}\rfloor$ 次,因为如果 $h_i$ 刚好能被 $a$ 整除,后面这个写法就错了。

    计算出了每个怪兽需要花费的机会那么就好做了,就变成了一个贪心问题,我们去尽可能得击杀需要的机会少的,当机会消耗完毕,得到的就是答案了。


代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>

using namespace std;

const int maxn=200005;
int n,a,b,k;
int f[maxn];
int cmp(int a,int b)
{
	return a<b;
}
int h[maxn],ans;
int main()
{
	scanf("%d%d%d%d",&n,&a,&b,&k);
	int p=a+b;
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&h[i]);
		h[i]%=p;
		if(h[i]==0)
		{
			h[i]+=b;
			f[i]=ceil((double)h[i]/a);
		}
		else f[i]=ceil((double)h[i]/a)-1;
	}
	sort(f+1,f+1+n,cmp);
	for(int i=1;i<=n;i++)
	{
		if(k-f[i]<0) break;
		ans++;
		k-=f[i];
	}
	printf("%d\n",ans);
}