【HDU 6855】Auto-correction

Description

给出一个长度为n的序列a,现在我们对这个序列进行m次如下的操作:(保证所有操作的操作区间互不相交)

  • 删除:删除l~r中的元素;
  • 替换:将l~r中的元素替换成给定的非空序列;
  • 插入:在第k个元素前面插入给定的非空序列;

设经过这m次操作后的序列为b,

请你给出一种合法的,只包含“替换”操作的操作序列,使得a经过这些操作后也能变成b,
要求给出的方案中,所有操作的∑(r−l)\sum (r-l)(rl)最小,如果相同则要求操作次数最少,
输出任意一种即可

保证序列a,b长度小于均4000

Solution

有一个比较显然的想法,即将输入的操作进行合并且转为“替换”操作
如果你这么想,那么恭喜你,你被带到坑里了,

因为题目只要求经过操作后的a,b序列相同,所以我们不需要完全按照题目给出的操作来做,

换句话说,题目大体上就是求:最长相同子序列

f[i][j]f[i][j]f[i][j]表示序列a,b分别做到第i,j位,∑(r−l)\sum (r-l)(rl)的最小值(当然还要记录一下操作次数和从哪转移来的)
O(n4)O(n^4)O(n4)的DP显然,

我们考虑先枚举j,再枚举i,这样就可以直接优化到O(n2)O(n^2)O(n2)级别

不过调整了枚举顺序就要考虑到寻址时间会暴涨,f[i][j]f[i][j]f[i][j]应调整为f[j][i]f[j][i]f[j][i]以降低寻址时间

复杂度:O(n2)O(n^2)O(n2)

PS:这题的读入也是够恶心的

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
using namespace std;
typedef long long LL;
const int N=4050,INF=1e9;
int read(int &n)
{
	bool q=0;n=0;char ch=' ';
	for(;ch!='-'&&(ch<'0'||ch>'9');ch=getchar());
	if(ch=='-')q=1,ch=getchar();
	for(;ch<='9'&&ch>='0';ch=getchar())n=(n<<1)+(n<<3)+ch-48;
	return q?n=-n:n;
}
int n,m,ans;
int B[N*10][3],B0;
int a[N],c[N];
int f[N][N],f0[N][N][2],f1[N][N];
int g[N];
int nw;
void GET()
{
	c[0]=0;
	char ch=' ';
	for(;1;)
	{
		for(;ch!='\n'&&ch!='\r'&&(ch<'A'||ch>'Z')&&(ch<'0'||ch>'9');)if(EOF==scanf("%c",&ch))return;
		if((ch<='Z'&&ch>='A')||ch=='\n'||ch=='\r')return;
		c[++c[0]]=0;
		for(;ch<='9'&&ch>='0';)
		{
			c[c[0]]=(c[c[0]]<<1)+(c[c[0]]<<3)+ch-48;
			if(EOF==scanf("%c",&ch))return;
		}
	}
}
bool BJ(int q,int w,int x,int y){return q>x||(q==x&&w>y);}
int main()
{
	freopen("!.in","r",stdin);
	freopen("1.out","w",stdout);
	int q,w,_;
	int SUM=0,SUM1=0;
	read(_);
	fo(i,1,4000)f[i][0]=f[0][i]=INF;
	// int ST=clock();
	while(_--)
	{
		read(n),read(m);
		B0=n+2;
		B[1][0]=2;
		fo(i,2,n+1)a[i-1]=read(B[i][1]),B[i][2]=i-1,B[i][0]=i+1;
		B[n+2][0]=B[n+2][1]=0;B[n+2][2]=n+1;
		char ch=' ';
		int I=1;
		fo(i,1,m)
		{
			for(ch=' ';ch<'A'||ch>'Z';ch=getchar());
			if(ch=='A')
			{
				read(q);
				for(;B[I][0]&&B[B[I][0]][2]<q;I=B[I][0]);
				GET();
				fo(j,1,c[0])
				{
					B[++B0][0]=B[I][0];
					B[B0][1]=c[j];
					B[B0][2]=0;
					B[I][0]=B0;
					I=B0;
				}
			}else if(ch=='D')
			{
				read(q),read(w);
				for(;B[B[I][0]][2]<q;I=B[I][0]);
				q=I;
				for(;I&&B[I][2]<=w;I=B[I][0]);
				B[q][0]=I;
				I=q;
			}else if(ch=='R')
			{
				read(q),read(w);
				GET();
				for(;B[B[I][0]][2]<q;I=B[I][0]);
				q=I;
				for(;I&&B[I][2]<=w;I=B[I][0]);
				fo(j,1,c[0])
				{
					B[++B0][0]=0;
					B[B0][1]=c[j];
					B[B0][2]=0;
					B[q][0]=B0;
					q=B0;
				}
				B[q][0]=I;
				I=q;
			}
		}
		m=0;
		for(int i=B[1][0];i;i=B[i][0])c[++m]=B[i][1];
		--m;
		fo(i,0,n)g[i]=0;
		fo(i,0,m)fo(j,0,n)f[i][j]=INF;
		f[0][0]=0;
		SUM+=n;
		SUM1+=n*m;
		fo(j,1,m)
		{
			int mi=0,mi1=0;
			fo(i,1,n)
			{
				f[j][i]=INF;
				if(a[i]==c[j])
				{
					f[j][i]=f[j-1][i-1];
					f0[j][i][0]=-1;
					f1[j][i]=f1[j-1][i-1];
				}
				if(BJ(f[j][i],f1[j][i],f[mi1][mi]+i-mi,f1[mi1][mi]+1))
				{
					f[j][i]=f[mi1][mi]+i-mi;
					f1[j][i]=f1[mi1][mi]+1;
					f0[j][i][0]=mi;
					f0[j][i][1]=mi1;
				}
				if(BJ(f[mi1][mi]-mi,f1[mi1][mi],f[g[i]][i]-i,f1[g[i]][i]))mi=i,mi1=g[i];
				if(BJ(f[g[i]][i],f1[g[i]][i],f[j][i],f1[j][i]))g[i]=j;
			}
		}
		// continue;
		printf("%d %d\n",f[m][n],f1[m][n]);
		q=m,w=n;
		for(;f0[q][w][0]==-1;--q,--w);
		for(;q&&w;)
		{
			printf("%d %d",f0[q][w][0]+1,w);
			fo(i,f0[q][w][1]+1,q)printf(" %d",c[i]);
			printf("\n");
			int Q=q;
			q=f0[Q][w][1];
			w=f0[Q][w][0];
			for(;f0[q][w][0]==-1;--q,--w);
		}

	}
	// printf("%d %d\n",SUM,SUM1);
	// printf("%d\n",clock()-ST);
	return 0;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值