Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第1张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第2张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第3张图片

题意:找出两个字符串最短的公共子字符串

分析:水题,最短一定是1,直接找一个一样的输出就行了。

代码:

#include 
using namespace std;
int ji[1005];
int a[1005];
int b[1005];
void rlmn()
{
	int n,m;
	scanf("%d%d",&n,&m);
	for (int i=0;i<=1000;i++)
		ji[i]=0;
	for (int i=0;i

水题,看题看了五分钟才确定真的这么水,被上场自闭场打怕了。

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第4张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第5张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第6张图片

题意:

给出一堆石子,两个人去选,只能在目前的第一堆中拿任意数量。求最后谁赢。

分析:这题稍微想了一会。第一,如果在一开始的第一堆石子中出现了1,那么主动权就会易手。后面一样,只要有1就会不停的易手,直到第一个不是1的石子堆出现,就一切都能控制了。后面的1不会产生任何影响,只要每次给对面留一个,然后在1的前一堆一次性全拿走逼他拿1.

因此找到不是1的石子堆的时候,这时候谁取谁就赢。而从第一堆开始每出现一个1就会易手一次。

特殊情况:全是1,不会有不是1的石子堆。但是也最简单,直接按奇数偶数输出就行了。

代码:

#include 
using namespace std;
int a[100005];
void rlmn()
{
	int n;
	int ans=0;
	scanf("%d",&n);
	for (int i=0;i

C1和C2是一样的题目,就是C2的n的数据范围从C1的1000变成了100000.因为我方法都是O(n)的,所以直接上C2的题了

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第7张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第8张图片

题意:

先定义一个操作,叫做前缀处理。就是选取字符串的一些前缀,然后把前缀先全部异或,再反转过来。比如001011做前缀3操作,则先变成110011再变成011011.如果做前缀6就是先变成110100,再变成001011.

分析:

你相信我真的是水题啊。我觉得这是最简单的一题。他不需要你给出最短的解,只要能成功就行了啊。

所以你知道A通过x1x2x3...xn变成了B,那么B可以通过xnxn-1...x2x1变成A。然后就好办了啊,你把两个字符串全部变成11111...11111(就是全部1的形式)然后正序输出第一个变化的,反序输出第二个变化的,就行了。

所以怎么变成11111...11111形式呢?只要你确保前k个数一样,然后反转就没用了,反转前后都一样。所以只异或,就是1变0,0变1.所以你只要按位处理,第k位如果和第k+1位不一样,就做一次前缀k,这样第k位就和第k+1位一样了。然后继续往后知道n-2位。第n位只要判断一下是不是0,如果是0再进行前缀n操作就行了。这样两个字符串就可以很轻松的变成11111...11111形式,问题就迎刃而解了。

代码:

#include 
using namespace std;
string s1,s2;
int ji1[100005];
int ji2[100005];
void rlmn()
{
	int n;
	scanf("%d",&n);
	cin>>s1;
	cin>>s2;

	int flag1=0;
	for (int i=0;i=0;i--)
	{
		printf(" %d",ji2[i]);
	}
	printf("\n");
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		rlmn();
	}
}

当我发现c2所谓hard其实就是n大了一些我很惊喜啊,直接复制粘贴上去结果re了。

因为一开始数组开的是1005...内存不够。。。

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第9张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第10张图片

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第11张图片

题意:定义操作merge为:每次从两个数组里面选小的第一个放进merge后的数组。然后给你一个2n数的排列,问能不能找出两个n长度的数组正好merge成它。是就YES,否则就NO

分析:水题啊!我忘记了0-1背包怎么写,找了40分钟板子,就差10分钟(赛后5分钟debug完成,但是longlong忘记用lld了要t一发)

首先我们找原来的数组,找出所有的极值段。极值段的定义是,第一个数比后面的数都大。让每个极值段都尽量长。

比如2314分成(2)(3,1)(4)

32615784分成(32)(615)(7)(84)

这样做的原因是极值段一定是某一个n长度数组的一部分。

然后问题来了,虽然我将它分成了好几段,比如32615784变成了4段,长度分别是2312,但是我怎么知道能不能分成两半呢?

问题转化成,给出n个数,问能不能从中选取一些数,和正好是m。

一开始想的是暴力回溯,直接打死。因为数据量再1e3,所以我尽量往O(n2)的算法想,想了半小时想出了01背包。只是这个01背包很特殊,就是物品的重量和体积是一样大的,背包的空间就是n。然后必须恰好装满,还要将dp初始化负无穷。

考虑到数也不小,因此开了longlong,然后将初始化负无穷定义位-2143000000

这样如果背包最后价值大于0说明恰好实现(而且价值应该也是n)。否则说明实现不了。

代码:

#include 
using namespace std;
typedef long long ll;
const int N=4015;
ll ji[N];
ll shu[N];
ll qu[N];
ll dp[N];
void rlmn()
{
	ll n;
	scanf("%lld",&n);
	for (ll i=0;i<2*n;i++)
	{
		scanf("%lld",&shu[i]);
	}
	for (ll i=0;i<=2*n;i++)
	{
		ji[i]=0;
		qu[i]=0;
		dp[i]=-2143000000;
	}
	ll shumax=2*n;
	for (ll i=2*n-1;i>=0;i--)
	{
		if (shu[i]!=shumax)
		{
			ji[shumax]++;
			ji[shu[i]]=-1;//变成儿子了 
		}
		else
		{
			ji[shu[i]]++;
			shumax--;
			while (ji[shumax]==-1)
			{
				shumax--;
			}
		}
	}
	ll num=1;
	for (ll i=0;i<=2*n;i++)
	{
		if (ji[i]>0)
		{
			qu[num++]=ji[i];
		}
	}
	num--;
	ll V=n;
	dp[0]=0;
	//解决一个0-1背包
	for (ll i=1;i<=num;i++)
	{
		for (ll j=V;j>=qu[i];j--)
		{
			dp[j]=max(dp[j],dp[j-qu[i]]+qu[i]);
		}
	} 
	if (dp[V]>0)
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
}
int main()
{
	int T;
	scanf("%d",&T);
	while (T--)
	{
		rlmn();
	}
}

 

本场比赛做出了4题。手速快点实际上五题也没问题。哎,不过无所谓,相当于要多打一场,多做一题最多到1400.下场上青!

不知道啥时候能打个蓝色哈。

赛中做了四题

Codeforces Round #658 (Div. 2) 比赛做出来的四题和差点做出来的一题_第12张图片

赛后把本来应该做出来的补了

得到一个教训:如果开了longlong输入%d是会t的!

你可能感兴趣的:(算法训练)