[HDOJ 3525][Vjude 10892] Orienteering [动态规划]

来源:互联网 发布:社会网络理论十讲 编辑:程序博客网 时间:2024/05/16 00:34

现有两排人,每排有n组人,每组人有m个,每个人到这排时,都先查看自己最喜欢的位置,如果他最喜欢的位置上有人了,他就去那个位置的后一个位置,如果那个位置还有人,就再去下一个,以此类推。

在每个人都找到了自己的位置之后,我们从左到右把每个人的组号编成一个序列,即一个n*m长度,每个元素都是1~n,每个数字出现了m次的序列。

现在求这两排人所形成的两个序列的最长公共子序列。

数据范围:n不超过10^4,m不超过10

定义状态dp[i]表示,在当前条件j下,第一个序列的整体和第二个序列的长度为j的前缀所能形成的长度为i的公共子序列的右侧的最左值为多少。

因为每个数出现的次数不超过m,所以每当j增加1的时候,最多仅有m个位置能够形成新的最长公共子序列,即最多更新m个位置的值,查找这m个位置使用二分查找

复杂度n*m*log(n)

#include <cstdio>#include <cstring>#include <algorithm>using namespace std;inline int in() {char c=getchar();while (c<'0'||c>'9') c=getchar();int ans=0;while (c>='0'&&c<='9') {ans=ans*10+c-'0';c=getchar();}return ans;}struct DisJoinSet {int a[200100];void clear(int n) {for (int i=0;i<=n;i++) a[i]=i;}int get(int i) {if (a[i]==i) return i;return a[i]=get(a[i]);}void tosame(int i,int j) {i=get(i);j=get(j);a[i]=j;}};int n,m,ans,nn;int s1[200010];int s2[200010];DisJoinSet next1,next2;void read(int s[],DisJoinSet &next) {int x,y,i,j;memset(s,0,sizeof(s1));next.clear(nn+nn);for (i=0;i<nn;i++) {x=in();y=in();y=next.get(y);s[y]=x;next.tosame(y,y+1);}for (i=0,j=0;i<nn;i++,j++) {while (s[j]==0) j++;s[i]=s[j];}}void print(int s[]) {for (int i=0;i<nn;i++)printf("%d ",s[i]);printf("\n");}int pos[10010][10];int num[10011];int dp[100010];int maxl;int main() {int t,tt,i,j,k;scanf("%d",&t);for (tt=1;tt<=t;tt++) {scanf("%d%d",&n,&m);nn=n*m;read(s1,next1);read(s2,next2);memset(num,0,sizeof(num));for (i=0;i<nn;i++) {pos[s1[i]][num[s1[i]]++]=i;}maxl=0;dp[0]=-1;//print(s1);//print(s2);for (j=0;j<nn;j++) {//printf("--%d %d\npos: ",j,s2[j]);for (k=m-1;k>=0;k--) {//printf("%d ",pos[s2[j]][k]);int *t=lower_bound(dp,dp+maxl+1,pos[s2[j]][k]);if (t==dp+maxl+1) maxl++;*t=pos[s2[j]][k];}//printf("\n");//for (k=0;k<=maxl;k++) printf("%d ",dp[k]);//printf("\n");}printf("Case #%d: %d\n",tt,maxl);}return 0;}


0 0