Toyota Programming Contest 2023#7(AtCoder Beginner Contest 328)G. Cut and Reorder(状压dp)

题目

长为n(n<=22)的序列a和b,第i个数分别对应ai和bi(1<=ai<=1e15,1<=bi<=1e15)

你可以执行以下两种操作任意次:

1. 将a序列分成若干段,然后可以任意重排这些段,

如果将a序列分成了x段,则代价是(x-1)*c,

比如,可以分成a1,a2a3,a4a5a6,然后重排为a2a3,a4a5a6,a1,代价是2*c

2. 选择a的任意一个位置,对其加k,代价是k的绝对值

求最小的总代价,使得a和b序列完全相等

思路来源

liouzhou101 & 官方题解

乱搞AC

题解

首先,考虑如果a的位置和b的位置在排列的位置上一一映射上了,

那么,比如a[i]和b[j]映射上了,那么通过第二种操作相等的额外代价就是abs(a[i]-b[j])

也就是说,这个分成x段并重排的操作只需要进行一次,剩下的都用第二种操作

所以,状压这个第一种操作的顺序

然后发现,如果A|BC|DE被重排成了DE|BC|A, 

也就是B在新序列的位置和A的位置不再前后相邻,D在新序列的位置和A的位置不再前后相邻

所以,一个比较自然的做法是,增序考虑选a的过程

假设当前选到了第i个,那么S集合内1的个数一定是i,

选择a的第i个的时候,需要考虑a的第i-1个选的是什么,从而判断二者是否前后相邻

于是就有,dp[S][i]表示(当前选了a序列前bit[S]个数),当前选择的b的集合是S,

当前a的最后一个数对应的b中的数选的是第i个时,二者只考虑这些数时完全相同的最小代价

那么转移就是如果上一个选的和当前选的前后相邻,就不额外创建一段

否则需要新起一段,加上c的代价,暴力这样做的复杂度O(n*n*2^n)

然后发现,假设前驱转移有x个,那么只有一个是不额外创建的,x-1个是额外创建的

不妨松弛一下答案,

也就是当成x个是额外创建的,1个是不额外创建的,

维护一下这x个前驱的最小值mn

用mn更新一次额外创建的答案,用这一个前驱更新一次不额外创建的答案,转移就O(1)了

复杂度O(n*2^n)

插曲(卡空间之乱搞AC)

交的时候,发现MLE了,因为空间也开了O(n*2^n)

时间给了2.8s,空间给了512M,但是22*2的22次方大概是700多M,long long改不了

把long long改成动态开的指针和回收结果TLE了

然后考虑把dp状态改成可回收的节点,用的时候取一个节点,不用的时候放回去

把空间压了压,相当于根据位从少到多bfs,

少的位的状态用过之后就从队列里干掉,达到复用空间的效果,类似循环队列

但是这么做肯定不是题目想让的样子,于是看了官方题解&别人的提交

正解

dp[S]表示当前选的集合是S,一次决策一段,把一段对应的代价c加上去

也就是取一段连续的a的区间[l,r],往b当前的集合中,没取并且也连续的一段区间里放

由于长为k的转移会出现2^{n-k}次,所以复杂度还是O(n*2^n)

手玩的时候没细想,以为多一个n

由于状态是从0开始拓展的,也就是拓展第一段的时候也会多算一个c,所以最终答案减c

代码1(正解)

#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<>j&1;
}
int main(){
	sci(n),scll(c);
	rep(i,0,n-1){
		scll(a[i]);
	}
	rep(i,0,n-1){
		scll(b[i]);
	}
	memset(dp,0x3f,sizeof dp);
	int up=(1<>j&1)bit++;
		}
		rep(j,0,n-1){
			if(!has(i,j)){
				ll v=dp[i]+c;
				int nex=i,x=0;
				while(j+x

代码2(乱搞AC)

#include
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<q;
void upd(ll &x,ll y){
	x=min(x,y);
}
int f(int v){
	if(~id[v])return id[v];
	int x=q.back();
	q.pop_back();
	memset(dp[x],0x3f,sizeof dp[x]);
	return id[v]=x;
}
void g(int x){
	q.push_back(id[x]);
}
bool has(int i,int j){
	return i>>j&1;
}
int main(){
	sci(n),scll(c);
	rep(i,0,n-1){
		scll(a[i]);
	}
	rep(i,0,n-1){
		scll(b[i]);
	}
	memset(id,-1,sizeof id);
	int up=(1<=0 && has(i,j-1)){
					upd(dp[f(i|(1<

你可能感兴趣的:(乱搞AC,#,状压dp/子集dp,状压dp,乱搞ac)