申金区间DP求改/求证假
查看原帖
申金区间DP求改/求证假
642173
KarmaticEnding楼主2025/1/14 17:09

思路是 fl,r,l1f_{l,r,l_1} 表示把区间 [l,r][l,r] 变成 [l1,l1+rl][l_1,l_1+r-l] 最小的代价,从两个方向转移:一个是对应位置直接加代价,一个是枚举断点交换对应区间。

#include<cstdio>
using namespace std;
__int128 f[23][23][23];
__int128 a[23],b[23];
inline __int128 abs(__int128 x){
	return x<0?-x:x;
}
inline __int128 min(__int128 H,__int128 G){
	return H<G?H:G;
}
template<typename _T>
inline void read(_T &x){
	char c;
	c=getchar();
	while(c<'0'||c>'9'){
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=(x<<3)+(x<<1)+(c&15);
		c=getchar();
	}
}
inline void write(__int128 x){
	if(x<(__int128)(10)){
		putchar(x|'0');
		return;
	}
	write(x/10);
	putchar((x%10)|'0');
}
int n;
__int128 c;
int main(){
	read(n);read(c);
	for(int i=1;i<=n;++i){
		read(a[i]);
	}
	for(int i=1;i<=n;++i){
		read(b[i]);
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=n;++j){
			f[i][i][j]=abs(a[i]-b[j]);
		}
	}
	for(int len=2;len<=n;++len){
		for(int l=1,r=len;r<=n;++l,++r){
			for(int l1=1;l1+len-1<=n;++l1){
				for(int k=0;k<len;++k){
					f[l][r][l1]+=abs(a[l+k]-b[l1+k]);
				}
				for(int k=0;k<len-1;++k){
					f[l][r][l1]=min(f[l][r][l1],f[l][l+k][l1+len-1-k]+f[l+k+1][r][l1]+c);
				}
			}
		}
	}
	write(f[1][n][1]);
	return 0;
}
2025/1/14 17:09
加载中...