Description
给出一个长度为n的序列a,现在我们对这个序列进行m次如下的操作:(保证所有操作的操作区间互不相交)
- 删除:删除l~r中的元素;
- 替换:将l~r中的元素替换成给定的非空序列;
- 插入:在第k个元素前面插入给定的非空序列;
设经过这m次操作后的序列为b,
请你给出一种合法的,只包含“替换”操作的操作序列,使得a经过这些操作后也能变成b,
要求给出的方案中,所有操作的∑(r−l)\sum (r-l)∑(r−l)最小,如果相同则要求操作次数最少,
输出任意一种即可
保证序列a,b长度小于均4000
Solution
有一个比较显然的想法,即将输入的操作进行合并且转为“替换”操作
如果你这么想,那么恭喜你,你被带到坑里了,
因为题目只要求经过操作后的a,b序列相同,所以我们不需要完全按照题目给出的操作来做,
换句话说,题目大体上就是求:最长相同子序列
设f[i][j]f[i][j]f[i][j]表示序列a,b分别做到第i,j位,∑(r−l)\sum (r-l)∑(r−l)的最小值(当然还要记录一下操作次数和从哪转移来的)
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;
}

1414

被折叠的 条评论
为什么被折叠?



