题解 CF1239E Turtle

题意

一只乌龟从 \(2 \times n\) 的棋盘的左上角走到右下角,只能往下或往右,需要给出一种方案来分配 \(2\times n\) 个数字使得乌龟走过的路径上的数之和的最大值最小。

\(2\le n\le 25\)\(0\le a_{1,i},a_{2,i}\le 50000\)

找性质

首先乌龟的路线一定是从 \((1,1)\) 走到 \((1,x)\),接着往下走到 \((2,x)\),然后向右走到 \((2,n)\)。其中 \(x\) 满足 \(1\le x\le n\)

性质 \(1\)第一行的数单调不降,第二行的数单调不升

证明:假设第一行存在 \(i,j\) 满足 \(i<j\)\(a_{1,i}>a_{1,j}\),那么交换这两个位置。容易发现当 \(x\)\([i,j)\) 这段区间时,答案变小了。否则不变。因此一定更优。第二行同理。

性质 \(2\)第一小的和第二小的数在 \((1,1)\)\((2,n)\)

证明:根据性质 \(1\),第一排 \((1,1)\) 最小,第二排 \((2,n)\) 最小。因此最小的一定在 \((1,1)\)\((2,n)\)。由于中心对称,不妨在 \((1,1)\)。第二小只能在 \((1,2)\)\((2,n)\)。容易发现 \(x=1\) 时在 \((2,n)\) 更优,\(x>1\) 时两者等价。因此第二小的数在 \((2,n)\)

性质 \(3\)\(x=1\)\(x=n\),即乌龟只会在 \((1,1)\)\((1,n)\) 往下走最优

证明:假设 \(1<x<n\),可知在 \(x\) 往下走的得分大于 \(x-1\)\(x+1\)。因此:\(a_{1,x}>a_{2,x-1}\)\(a_{1,x+1}<a_{2,x}\)。推导一下:\(a_{1,x}>a_{2,x-1}\ge a_{2,x}>a_{1,x+1}\),所以 \(a_{1,x}>a_{1,x+1}\),不符合性质 \(1\)。因此 \(x=1\)\(x=n\)

然后…

首先填入 \((1,1)\)\((2,n)\)。剩下的 \(2\times n-2\) 个数分成两组 \(n-1\) 个数,要求最大值最小,也就是差最小。于是暴力跑背包加上 bitset 优化。复杂度 \(O(\frac{n^2\times \sum a}{w})\)

代码

如果不输出方案的话,大概长这样…

int main()
{
	n=read();
	for (int i=1;i<=2*n;i++) a[i]=read();
	sort(a+1,a+n*2+1);
	dp[0][0]=1;
	for (int i=3;i<=2*n;i++)
	{
		int u=min(i-2,n-1);
		s+=a[i];
		for (int j=u;j>=1;j--)
		{
			dp[j]|=(dp[j-1]<<a[i]);
		}
	}
	int ans=1e9;
	for (int i=0;i<=s;i++)
	{
		if (dp[n-1][i]) ans=min(ans,max(i,s-i));
	}
	cout << a[1]+a[2]+ans << endl;
	return 0;
}

然后惊奇地发现输出方案不是很好弄。于是我们把背包的滚动数组还原,增加原来的第一维。这样就可以输出方案了!

//注意:read、输出方案部分已省略
#include <bits/stdc++.h>
using namespace std;
const int N=26,M=50005;
int n; 
int a[N*2],s,p[N];
bitset<M*N*2>dp[2*N][N];
bool used[2*N];
int main()
{
	n=read();
	for (int i=1;i<=2*n;i++) a[i]=read()+1;
	sort(a+1,a+n*2+1);
	dp[2][0][0]=1;
	for (int i=3;i<=2*n;i++)
	{
		int u=min(i-2,n-1);
		s+=a[i];
		for (int j=u;j>=1;j--)
		{
			dp[i][j]=dp[i-1][j]|(dp[i-1][j-1]<<a[i]);
		}
		dp[i][0]=dp[i-1][0];
	}
	int ans=1e9;
	for (int i=0;i<=s;i++)
	{
		if (dp[2*n][n-1][i]) ans=min(ans,max(i,s-i));
	}
	int tot=2*n,u=n-1;
	while (ans)
	{
		if (dp[tot-1][u][ans]) tot--;
		else used[tot]=1,ans-=a[tot],tot--,u--;
	}
	//used=0的是一组,used=1的是另一组
	//不要忘了a[1]和a[2]
	return 0;
}

这个做法比不用 bitset 的 \(O(n^2\times\sum a)\) 快多了。

posted @ 2021-10-04 21:10  Little09  阅读(75)  评论(0)    收藏  举报