[Codeforces1239E]Turtle

题意

给你一个 2 × n 2\times n 的棋盘,经过格子 ( i , j ) (i,j) 就会获得权值 a i , j a_{i,j}

重新排列整个棋盘,使得从左上角走到右下角的路径权值和的最大值最小(只能向下或者向右走)

2 n 25 , 0 a i , j 5 × 1 0 4 ( 1 i 2 , 1 j n ) 2\le n\le25,0\le a_{i,j}\le5\times10^4(1\le i\le2,1\le j\le n)


题解

考虑交换同一行两个数的位置对最大值的影响
在这里插入图片描述
变成
在这里插入图片描述
那么有的路径权值和不会改变,有的路径取值和会减小,这样路径最大值就有可能减小

所以第一行升序排序更优,同理可得第二行降序排列更优

考虑对于一种已经排好序的棋盘答案多少

考虑两个相邻路径的权值和的差异

x 1 x_1 x i x_i x i + 1 x_{i+1} x n x_n
y 1 y_1 y i y_i y i + 1 y_{i+1} y n y_n

对于路径 x 1 . . . x i y i . . . y n x_1\rightarrow...\rightarrow x_i\rightarrow y_i\rightarrow...\rightarrow y_n 和路径 x 1 . . . x i x i + 1 y i + 1 . . . y n x_1\rightarrow...\rightarrow x_i\rightarrow x_{i+1}\rightarrow y_{i+1}\rightarrow...\rightarrow y_n

他们的权值差的绝对值就是 Δ i = x i + 1 y i \Delta_i=|x_{i+1}-y_i| ,由于 x x 递增, y y 递减

所以 Δ i \Delta_i 的函数图像就长这样在这里插入图片描述

所以最大值就一定会在两边取到,即最大值为 max ( i = 1 n x i + y n , x 1 + i = 1 n y i ) = x 1 + y n + max ( i = 2 n x i , i = 1 n 1 y i ) \max(\sum_{i=1}^nx_i+y_n,x_1+\sum_{i=1}^ny_i)=x_1+y_n+\max(\sum_{i=2}^nx_i,\sum_{i=1}^{n-1}y_i)

把最小的两个数拿走,问题就转化成了:给你一个有 m m 个数的序列 a a ,将其分成两个等大的集合 A , B A,B ,最小化 max ( A , B ) \max(\sum A,\sum B)

这个问题实质上就是一个背包问题

先把 a a 从小到大排序,设 S u m = a Sum=\sum a

f [ i ] [ j ] [ k ] f[i][j][k] 表示前 i i 个数中放 j j 个数在集合 A A 中,且 A = k \sum A=k 的情况是否存在

那么 f [ i ] [ j ] [ k ] = f [ i 1 ] [ j ] [ k ] f [ i 1 ] [ j 1 ] [ k a i ] f[i][j][k]=f[i-1][j][k]|f[i-1][j-1][k-a_i]

那么 A n s = min { max ( k , S u m k ) } Ans=\min\{\max(k,Sum-k)\} ,可见 A n s Ans 越靠近 S u m 2 \frac{Sum}2 越小

然后根据 A n s Ans 寻找转移路径输出答案即可

一个小优化是 f f 的转移可以用 C + + C++ b i t s e t bitset 实现

时间复杂度 O ( 1 64 n 2 a ) O(\frac1{64}n^2\sum a)

#include<bits/stdc++.h>
#define fp(i,a,b) for(register int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(register int i=a,I=b-1;i>I;--i)
#define file(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
using namespace std;
const int N=55,M=50*5e4+5;
typedef long long ll;
int n,m;
bitset<M>f[52][26];
int main(){
	#ifndef ONLINE_JUDGE
		file("s");
	#endif
	scanf("%d",&n);m=(--n)<<1;
	vector<int>a(m+3),Ans[2];
	fp(i,1,m+2)scanf("%d",&a[i]);
	sort(a.begin()+1,a.end());
	Ans[0].push_back(a[1]);a.erase(a.begin()+1);
	Ans[1].push_back(a[1]);a.erase(a.begin()+1);
	f[0][0][0]=1;
	fp(i,1,m){
		f[i][0][0]=1;
		fp(j,1,min(n,i))
			f[i][j]=f[i-1][j]|(f[i-1][j-1]<<a[i]);
	}
	int Cur=accumulate(a.begin()+1,a.end(),0)>>1,Cnt=n;
	while(!f[m][n][Cur])--Cur;
	fd(i,m,1)
		if(a[i]>Cur||!f[i-1][Cnt-1][Cur-a[i]])
			Ans[0].push_back(a[i]);
		else Ans[1].push_back(a[i]),Cur-=a[i],--Cnt;
	sort(Ans[0].begin(),Ans[0].end());
	sort(Ans[1].begin(),Ans[1].end(),greater<int>());
	fp(i,0,1)fp(j,0,n)printf("%d%c",Ans[i][j]," \n"[j==n]);
return 0;
}

猜你喜欢

转载自blog.csdn.net/BeNoble_/article/details/102678628