【bzoj3072: [Pa2012]Two Cakes】dp


3072: [Pa2012]Two Cakes

Time Limit: 10 Sec   Memory Limit: 128 MB
Submit: 241   Solved: 72
[ Submit][ Status][ Discuss]

Description

有两个长度为n的排列(1<=n<=1,000,000),然后你要再次写出一模一样的两个排列,于是你的左手和右手同时拿笔开始写。但是为了锻炼自己的协调能力,你不想左手和右手同时在写一模一样的数,每写一个数你就需要花1ms的时间,那么你要写完这两个序列至少要花多久时间呢?注:每个序列同时只准用一只手写。
 

Input

Output

Sample Input

3 --n
1 2 3 --第一个排列
3 2 1 --第二个排列

Sample Output


4

HINT



解释:

  第一ms:1   3

  第二ms:2   X

  第三ms:3   2

  第四ms:X   1

X表示不写。

Source

、、claris推荐的动规题果然不是我等凡人可以挑战的。

很明显可以想到O(n^2)的动规思路:

dp[i][j]=第一列数写到第i个,第二列数写到第j个的最少时间

转移:a[i]==b[j]时 dp[i][j]=min(dp[i+1][j],dp[i][j+1]);

  a[i]!=b[j]时 dp[i][j]=dp[i+1][j+1];

然后判断下是否是边界就好了。

在此基础上我们做一定的优化。

1、由于是n个数的排列(之前没注意到这一点所以怎么都想不出来),所以a[i]==b[j]的情况最多只有n种

2、a[i]!=b[j]的情况我们就是可以无脑往后走,直到走到a[i+k]==b[j+k]的时候停止,所以我们可以想个办法直接走到i+k这个位置。O(p)(p是多少后面有说)

所以我们都只用考虑a[i]==b[j]的情况(不是该情况的可以用O(p)的效率转移到这里),一共就是n种状态,O(p)效率转移,

总效率为O(n*p);

问题就变成了我们如何在(i,j)状态下转移到(x,y)(x=i+k,y=j+k)的状态下,我们可以知道,i-j==x-y,所以我们就根据i-j这个位置的差值进行二分查找,找到i后面第一个位置Pos,满足a[Pos]==b[Pos1]&&Pos-Pos1==i-j。

就是对于a数组中每个位置,记录下与b数组中该数所在位置的差值,然后对于每个差值,我们把位置从小到大记录下来,就可以对于同一个差值里二分查找,找到接下来的第一个位置。(感觉语文水平受到了制约,不理解的看代码吧)

刚开始的时候我是多插入了2*n个数,然后排序了下,发现会T,然后参考了claris大佬的代码,终于ac。

#include<cstdio>
#include<algorithm>
#include<iostream>
#define N 1000005
using namespace std;
int mp[N],a[N],b[N],p[N],n,t,l,r,mid,s[2*N],e[2*N],now,tmp,q[N*2],nxt[N*2],fir[N*2],m;
int find(int l,int r,int x){
	while(l<r){
		mid=(l+r)/2;
		if(q[mid]<x) l=mid+1;
		else r=mid;
	}
	if(q[r]>x) 
	return r;else return r+1;
}
int dfs(int x,int y){
	if(a[x]==b[y]){
		if(mp[x])return mp[x];
		t=min(dfs(x+1,y),dfs(x,y+1))+1;
		mp[x]=t;
		return t;
	}else{
		int P=find(s[x-y+n],e[x-y+n],x);
		if(P==e[x-y+n]+1){
			t=max(n-x+1,n-y+1);
			return t;
		}
		t=dfs(q[P],q[P]-x+y)+(q[P]-x);
		return t;
	}
}
inline char nc(){
    static char buf[100000],*p1=buf,*p2=buf;
    return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;
}
inline int _read(){
    char ch=nc();int sum=0;
    while(!(ch>='0'&&ch<='9'))ch=nc();
    while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
    return sum;
}
void add(int l,int r){nxt[r]=fir[l];fir[l]=r;}
int main(){
	freopen("1.in","r",stdin);
	freopen("1.out","w",stdout);
	n=_read();
	for(int i=1;i<=n;i++) a[i]=_read();
	for(int i=1;i<=n;i++) b[i]=_read(),p[b[i]]=i;
	for(int i=n;i;i--) add(i-p[a[i]]+n,i);
	int j;
	for(int i=1;i<n+n;e[i++]=m)for(s[i]=m+1,j=fir[i];j;j=nxt[j])q[++m]=j;
	e[n+n-1]=m;
	printf("%d\n",dfs(1,1));
}


猜你喜欢

转载自blog.csdn.net/bingoo0o0o/article/details/79289939