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\) 序列长。
每次加入一个新元素:

  1. 枚举所有已有的 \(i\)
  2. 找到下一个匹配字符,更新。

其中操作二可以预处理出来,可 \(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;
}
posted @ 2026-02-08 16:15  2025ing  阅读(1)  评论(0)    收藏  举报