Codeforces Round 939 (Div. 2)D. Nene and the Mex Operator 状压 ,难搞的递归

Problem - D - Codeforces

推荐阅读:Codeforces Round 939 (Div. 2)(A-D) - 知乎

状压:

18个数,可以状压。

因为无法确定哪种方案最后和最大。

举例:

0 0 0 5 0 0

你说是一起呢,还是左右各自弄呢?

递归:

这是一个“有规律的”过程:

0 0 0 0
0 0 0 1
0 0 2 2
0 0 0 2
0 0 1 2
0 3 3 3
0 0 0 3
0 0 1 3
0 2 2 3
0 0 2 3
0 1 2 3
4 4 4 4

————

观察:

0 0 0 0
0 0 0 1
0 0 2 2
0 0 0 2
0 0 1 2
0 3 3 3
0 0 0 3
0 0 1 3
0 2 2 3
0 0 2 3
0 1 2 3
4 4 4 4

感觉可以递归,但是递归非常难想。同时又想不出其他办法。

可以直接看代码理解:

0.ptr就是最后打印的步骤,我们可以先存起来,这样就知道有多少步了。

1. 调用前先清零,浪费步骤就浪费了,步数是足够多的。检查如果有0,那么我们对总体操作是不会变为0的,那就对总体操作两次。

2. 调用,看我们上述过程,其实是右往左这么一个过程。(必须先搞出来大的数)

3. op(l +1, r ) ; 就是向右的一个过程。退出来的时候就是向左。

我们让这个出来后符合顺序。

只有一位时,已经满足了,对这个“整体”操作即可。(何尝不是一个整体)

当只有两位时也已经满足,多位就有关我们后面的操作了。

既然两位时已经满足 0 1,符合顺序mex,那就对他们操作成为 2 2 吧。

然后接下来我们希望最后一个2 的 左边也是顺序呀,我们的l正好能到1的位置哦。而前面都是0,。(是不是某种巧合。可以模拟一下)

(出题人讲的是从左往右倒着来的,整体思路一样,不过没有说的很透彻【官方讲解】Codeforces Round 939 (Div. 2)_哔哩哔哩_bilibili

4. 最大后,如果是结束(cl,cr在调用前就赋值了),那就退出了。

否则得接着操作,仍旧参照上述过程,可以发现左边清零成为子问题接着搞就可以。

vector<PII>prt;
int cl, cr;
void op(int l, int r)
{
	if (l == r)
	{
		prt.emplace_back(l, r);
		return;
	}

	op(l + 1, r);//出来就是顺序啦
	prt.emplace_back(l, r);
	if (l == cl && r == cr) return;
	prt.emplace_back(l, r-1);
	op(l, r - 1);
}

参考代码:

也是学到了emplace_back的效率高点。

注意emplace_back特殊类型(比如pair<int,int>)不需要初始化列表{},直接括号里写多个值就可以了。

#define ll long long
#define endl "\n"
#define PII pair<int,int>
#define int long long

const int maxn = 2e3 + 5;
int arr[maxn];

vector<PII>prt;
int cl, cr;
void op(int l, int r)
{
	if (l == r)
	{
		prt.emplace_back(l, r);
		return;
	}

	op(l + 1, r);//出来就是顺序啦
	prt.emplace_back(l, r);
	if (l == cl && r == cr) return;
	prt.emplace_back(l, r-1);
	op(l, r - 1);
}

void solve()
{
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
	{
		cin >> arr[i];
	}
	int ans = 0;
	int mask = 0;
	for (int i = 0; i < (1<<n); i++)
	{
		int cursum = 0;
		for (int l = 0; l < n; l++)
		{
			if ((i >> l) & 1)
			{
				int r = l;
				while (((r+1) < n) &&((i >> (r+1)) & 1))
				{
					r++;
				}
				cursum += (r - l + 1) * (r - l + 1);
				l = r;
			}
			else cursum += arr[l];
		}
		if (cursum > ans)
		{
			ans = cursum;
			mask = i;
		}
	}

	for (int l = 0; l < n; l++)
	{
		if ((mask >> l) & 1)
		{
			int r = l;

			bool zero = arr[l] == 0;

			while ((r + 1 < n) && ((mask >> (r+1)) & 1) )
			{
				r++;
				zero |= arr[r] == 0;
			}

			if (zero)
				prt.emplace_back(l + 1, r + 1);

			prt.emplace_back(l+1, r+1);
			cl = l + 1, cr = r + 1;
			op(l+1, r+1);
			l = r;
		}
	}

	cout << ans << " ";//<< cnt << endl;
	cout << prt.size() << endl;
	for (auto p : prt)
	{
		cout << p.first << " " << p.second << endl;
	}
}

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值