题目描述:
有 n n n 个插座, m m m 个设备和 k ( n , m , k ≤ 100 ) ( n , m , k ≤ 100 ) k (n, m, k \leq 100)(n,m,k \leq 100) k(n,m,k≤100)(n,m,k≤100)种转换器,每种转换器有无限多。已知每个插座的类型,每个设备的插头类型,以及每种转换器的插座类型和插头类型。插头和插座类型都用不超过 2424 个字母表示,插座只能插到类型名称相同的插座中。 例如,有 44 个插座,类型分别为 A,B,C,D;有 5 个设备,插头类型分别为 B,C,B,B,X;还有三种转换器,分别是 B->X,X->A 和 X->D。这里用 B -> X 表示插座类型为 BB,插头类型为 XX,因此一个插座类型为 BB 的设备插上这种转换器之后就 “变成” 了一个插头类型为 X 的设备。转换器可以级联使用,例如插头类型为 AA 的设备依次接上 A->B, B->C, C->D 这 33 个转换器之后会 “变成” 插头类型为 DD 的设备。 要求插的设备尽量多。问最少剩几个不匹配的设备。
解题思路:
这题思路比较简单,主要考察二分图的最大匹配算法。我们只需先求出每个电器通过转换器能插哪几个插头,那么问题就变成有
n
n
n个插头,
m
m
m个电器,并且告诉你每个电器能插哪几个插头,求最大同时能插几个电器。这就变成了裸的二分图最大匹配题目,我们可以使用相应的算法来完成,我这里使用了匈牙利算法。
需要注意的是:插头类型可能不止100个,最多可能有400个。还有就是输出格式,每组数据之间输出一个空行。
代码:
#include <bits/stdc++.h>
using namespace std;
int t,a[405][405],x[105],y[105];
int book[105],match[105];
map<string,int> mp;
int n,m,k;
int dfs(int p)
{
for(int i=1;i<=n;i++)
{
int to=x[i];
if(book[i]||!a[y[p]][to])
continue;
book[i]=1;
if(!match[i]||dfs(match[i]))
{
match[i]=p;
return 1;
}
}
return 0;
}
int main(){
cin>>t;
while(t--)
{
mp.clear();
memset(a,0,sizeof(a));//存图,a[i][j]表示插头是i的电器 能否 插j插头
memset(x,0,sizeof(x));//插头
memset(y,0,sizeof(y));//电器
//输入插头
cin>>n;
int cnt=0;
for(int i=1;i<=n;i++)
{
string s;
cin>>s;
if(!mp[s])
mp[s]=++cnt;
x[i]=mp[s];
}
//输入电器
cin>>m;
for(int i=1;i<=m;i++)
{
string s1,s2;
cin>>s1>>s2;
if(!mp[s2])
mp[s2]=++cnt;
y[i]=mp[s2];
a[y[i]][y[i]]=1;
}
//输入转换器
cin>>k;
for(int i=0;i<k;i++)
{
string s1,s2;
cin>>s1>>s2;
if(!mp[s1])
mp[s1]=++cnt;
if(!mp[s2])
mp[s2]=++cnt;
a[mp[s1]][mp[s2]]=1;
}
//floyd算法
for(int k=1;k<=cnt;k++)
for(int i=1;i<=cnt;i++)
for(int j=1;j<=cnt;j++)
a[i][j]=a[i][j]||(a[i][k]&&a[k][j]);
//匈牙利算法
int ans=0;
memset(match,0,sizeof(match));
for(int i=1;i<=m;i++)
{
memset(book,0,sizeof(book));
if(dfs(i))
ans++;
}
cout<<m-ans<<endl;
if(t)cout<<endl;
}
return 0;
}