Polycarp and Div 3 CodeForces - 1005D

题面链接:http://codeforces.com/contest/1005/problem/D

这道题大家应该都知道如果数字之和等于三就肯定能被三整除了吧,但是这道题难就难在怎么使得可能在数字中间有某几个数字我们不要来求到最大值,题解的思路是我们不妨计算从第一个到第i - 1个的所有数字之和对3求余,如果这个数之前也出现(假设为位置j,j < i),那么从j到i的这些数字之和一定能够整除以3,因为从j到i之间的数字之和必然是3的k(0 <= k)倍。具体这样做的道理是这样的,假设现在得到前i个数的和对3求余为r,我们知道可能从j到i产生了一组能整除3的数,但是这个是不是最大的我们还得和之前的比较,我们设立一个z数组,用于存储前i-1个数中能切割成整除3的最大个数,这样,在z[i]时有两种选择,第一种通过j来进行拓展,z[i] = z[j] + 1,第二中不这样选择,那么前i-1个最大个数就等于前i-2个的最大切割个数,z[i] = z[i - 1],我们从两种情况中选择最大的和即可。

注意就是,一定要特判是不是第一次出现余数为0,1,2的值(0比较特殊,当第一次出现余数为1,2时个数仍然是0,但是如果第一次出现余数为0时,个数就是1了,因为余数为0刚好能整除3),再一个就是设立一个数组记录最近的余数为r的答案,方便直接判断。

AC代码:

#include <bits/stdc++.h>
using namespace std;
char s[200005];
int last_vis[3];//最近的余数为i的时候的答案 
int last, temp;//temp代表从第1个到当前数字的和对3求模的值,last代表从第一个到上一个的时候的答案 
int main()
{
	scanf("%s", s);
	memset(last_vis, -1, sizeof(last_vis));//先全部没出现过,下方如果为-1就说明是第一次出现 
	last_vis[0] = 0;//但是当是3的倍数时第一次出现也要算,所以先赋值为0 
	int size = strlen(s);
	for(int i = 1; i <= size; i++)
	{
		temp += s[i - 1] - '0';
		temp %= 3;
		if(last_vis[temp] != -1)//如果不是第一次出现余数为temp的值 
		{
			last = max(last, last_vis[temp] + 1);//从上一个余数为temp的值进行拓展,或者不拓展直接跳过 
		}
		last_vis[temp] = last;//记得无论如何更新余数为temp的最近的答案,因为我们之后遇到余数为temp的时候要找到的是在遇到之前最近的余数为temp的答案,使其加一来拓展的。 
	}
	printf("%d\n", last);
	return 0;
}

  其实这道题重点就是拓展关系怎么找,要么不要现在的这个数字,要么通过前面离现在的数字最近的且余数一样的答案进行拓展,拓展的话就是加1,也就是从j到i的数字组成的数可以整除3,所以前i个的比前j个的多一。

原题解:http://codeforces.com/blog/entry/60511

posted @ 2020-05-19 18:35  funforever  阅读(129)  评论(0编辑  收藏  举报