题意:
一个最大为2000*2000的矩阵,有h个横着的单词,v个竖着单词,横着的可能与竖着的有交叉,交叉的字符只能选其一放在格子内,问怎样选择才能使矩阵内单词的数量最多,输出最多有多少个。(注意同一行的单词不会有交叉)
解法:
最大独立集,因为横着的与横着不会有交叉,竖着的没有,所以可以把横着的单词放在一边,竖着的放在另一边,横着的与竖着的交叉点是不同字母的进行连边。跑一遍二分匹配,每一个匹配就可以理解成这条边匹配的两个点只能有一个成立。最后的答案就是总共的点数减去匹配数就行了。
#include <bits/stdc++.h>
using namespace std;
const int N = 2000+10;
struct node{
int v,next;
}E[N*100];
int h,v,top;
char s[N][N]; ///存单词的图
int head[N];
int num[N][N]; ///存单词的编号
bool vis[N];
int match[N]; ///存二分匹配的点
void Init()
{
top = 0;
for(int i = 0;i < N;i++){
head[i] = match[i] = -1;
for(int j = 0;j < N;j++){
s[i][j] = '\0';
num[i][j] = 0;
}
}
}
void add(int u,int v)
{
E[top].v = v;
E[top].next = head[u];
head[u] = top++;
}
bool dfs(int u)
{
for(int i = head[u];i != -1;i = E[i].next){
int v = E[i].v;
if(vis[v]) continue;
vis[v] = true;
if(match[v] == -1 || dfs(match[v])){
match[v] = u;
return true;
}
}
return false;
}
void xyl()
{
int ans = 0;
for(int i = 1;i <= h;i++){
memset(vis,false,sizeof vis);
if(dfs(i)){
ans++;
}
}
ans = h+v-ans;
printf("%d\n",ans);
}
int main(void)
{
int T;
scanf("%d",&T);
char str[1010];
while(T--){
Init();
scanf("%d%d",&h,&v); ///总共有h+v个点
for(int i = 1;i <= h;i++){
int x,y;
scanf("%d%d%s",&x,&y,str);
swap(x,y);
int len = strlen(str);
for(int j = 0;j < len;j++){
if(s[x][y+j] != str[j] && num[x][y+j] != 0){
add(num[x][y+j],i);
add(i,num[x][y+j]);
}
s[x][y+j] = str[j];
num[x][y+j] = i;
}
}
for(int i = 1;i <= v;i++){
int x,y;
scanf("%d%d%s",&x,&y,str);
swap(x,y);
int len = strlen(str);
for(int j = 0;j < len;j++){
if(s[x+j][y] != str[j] && num[x+j][y] != 0){ ///只有横着与竖着交叉点字符不一样才建边
add(num[x+j][y],h+i);
add(h+i,num[x+j][y]);
}
s[x+j][y] = str[i];
num[x+j][y] = h+i;
}
}
xyl(); ///匈牙利
}
return 0;
}