IR 二分图匹配(转化,LCS,VAI)
评测链接:Link
有两种类型的点—— \(n\) 个红点、\(m\) 个蓝点(\(1\le n\le 10^3,1\le m\le 10^6\)),
红点编号为 \(1\sim n\) ,蓝点编号为 \(1\sim m\) ,
每个点都有一个属性值,用大写字母 \(A\sim Z\) 表示,每次要将属性相同蓝点和红点进行匹配操作,
每个点只能匹配一次,每次匹配的结点不能存在交叉的情况,
即如果出现红点 \(i_1\) 与蓝点 \(j_1\) 匹配,红点 \(i_2\) 与蓝点 \(j_2\) 匹配,且满足 \(i_1<i_2\) ,那么一定有 \(j_1<j_2\)。
求最多有多少组匹配。Input 01 2 5 AB BAABB Output 01 2
有前面几道题打底,匹配操作大家已经熟悉了,是一道 LCS 板子。
设计 \(f(i,j)\) 表示用掉 \(A\) 序列前 \(i\) 位和 \(B\) 序列前 \(j\) 位所得最长长度。
有 \(f(i,j)=\max\set{f(i-1,j),f(i,j-1),f(i-1,j-1)+[A_i==B_j]}\),
时间复杂度 \(O(nm)\)。
问题是什么?
\(A_i==B_j\) 的情况太少了,大部分循环无意义。
用 VAI 压缩一下,发现这个一长一短,先试一试。
设 \(f(i)\) 表示答案为 \(i\) 时用去 \(B\) 序列长。
每次加入一个新元素:
- 枚举所有已有的 \(i\)。
- 找到下一个匹配字符,更新。
其中操作二可以预处理出来,可 \(O(1)\) 查询。
\(f(i)\) 压长串,这么去做,时间复杂度就达到了 \(O(n^2)\),可以通过。
#include<bits/stdc++.h>
using namespace std;
const int N=1033,M=1000033;
int n,m,nxt[M][26],dp[N],cnt=0;
char A[N],B[M];
int main(){
freopen("match.in","r",stdin);
freopen("match.out","w",stdout);
scanf("%d%d%s%s",&n,&m,A+1,B+1);
for(int i=1;i<=N-1;i++) dp[i]=M;
for(int x=0;x<26;x++) nxt[m][x]=M;
for(int i=m-1;i>=0;i--){
for(int x=0;x<26;x++) nxt[i][x]=nxt[i+1][x];
nxt[i][B[i+1]-'A']=i+1;
}
for(int i=1;i<=n;i++){
for(int j=cnt;j>=0;j--)
if(nxt[dp[j]][A[i]-'A']<=m)
dp[j+1]=min(dp[j+1],nxt[dp[j]][A[i]-'A']);
if(dp[cnt+1]<=m) ++cnt;
}
printf("%d\n",cnt);
return 0;
}

浙公网安备 33010602011771号