Description
共有 n n 名队员,队伍里已有名队员 a1,...,ak a 1 , . . . , a k ,但是期望队伍里有的队员是 b1,...,bk b 1 , . . . , b k ,已知矩阵 Ai,j A i , j ,其中 Ai,j=1 A i , j = 1 表示队伍中的 i i 队员可以换成队员( i i 队员在队伍里且队员不在队伍里的前提下),问是否存在一个换人序列可以把这 k k 名队员换成理想的名队员
Input
第一行两个整数 n,k n , k 表示队员总数和队伍中队员数量,之后 k k 个整数表示已在队伍的队员,之后 k k 个整数表示期望在队伍的队员,最后一个 n×n n × n 的 01 01 矩阵 A A
Output
如果存在一个合法的换人序列则输出 YES Y E S 和换人操作次数以及具体操作,否则输出 NO N O
Sample Input
5 2
1 2
4 5
00100
00100
00011
00000
00000
Sample Output
YES
4
1 3
3 4
2 3
3 5
Solution
首先用 Floyd F l o y d 把一步可达矩阵变成可达矩阵,对于已有队员集合和期望队员集合,去掉其中相同的队员,假设剩下 m m 个人,根据可达矩阵对这对人建二分图求最大匹配,如果最大匹配小于 m m 说明无解,否则说明有一种方案可以把已有的个人分别与期望的 m m 个人一一对应,但是注意到这个一一对应事实上是 m m 条换人的路径,而这条路径可能会交叉,例如在把已有一名队员变成期望的一名队员时,交换路径中可能出现了一个也在已有队员集合中的队员,这是不合法的,所以在考虑具体方案时需要做一些处理,在依次处理这 m m 条路径时,对于当前处理的路径,假设从到 a a (用数字表示在队伍中的队员,用小写字母表示不在队伍中的队员)的路径是:
如果直接按顺序把这个换人序列输出的话,由于 2 2 已经在队伍中,不可能用把 2 2 再换到队伍中,所以要对这个序列做一些处理,注意到只要我们把该操作序列变成和 1→b→c→2 1 → b → c → 2 就可以避免冲突,即从目标点往前,遇到一个在队伍中的点就把已有操作序列输出,之后继续往前直至到起点,如此操作可以使得从 1 1 换到的过程中,只改变了 1 1 和的归属而不会影响其他点
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=305;
int uN,vN;//u,v数目
int g[maxn][maxn];//编号是0~n-1的
int linker[maxn];
bool used[maxn];
bool dfs(int u)
{
int v;
for(v=0;v<vN;v++)
if(g[u][v]&&!used[v])
{
used[v]=true;
if(linker[v]==-1||dfs(linker[v]))
{
linker[v]=u;
return true;
}
}
return false;
}
int hungary()
{
int res=0;
int u;
memset(linker,-1,sizeof(linker));
for(u=0;u<uN;u++)
{
memset(used,0,sizeof(used));
if(dfs(u)) res++;
}
return res;
}
int n,k,m[maxn][maxn],G[maxn][maxn],va[maxn],vb[maxn],vis[maxn];
char s[maxn];
vector<P>ans,S;
int dfs(int u,int v)
{
vis[u]=1;
if(u==v)return 1;
for(int w=1;w<=n;w++)
if(G[u][w]&&!vis[w])
{
if(dfs(w,v))
{
S.push_back(P(u,w));
if(va[u])
{
for(int i=S.size()-1;i>=0;i--)ans.push_back(S[i]);
S.clear();
}
return 1;
}
}
return 0;
}
int main()
{
while(~scanf("%d%d",&n,&k))
{
memset(va,0,sizeof(va));
memset(vb,0,sizeof(vb));
int temp;
for(int i=0;i<k;i++)scanf("%d",&temp),va[temp]=1;
for(int i=0;i<k;i++)scanf("%d",&temp),vb[temp]=1;
for(int i=1;i<=n;i++)
{
scanf("%s",s+1);
for(int j=1;j<=n;j++)
G[i][j]=m[i][j]=s[j]-'0';
}
for(int l=1;l<=n;l++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(m[i][l]&&m[l][j])m[i][j]=1;
memset(g,0,sizeof(g));
int res=0;
for(int i=1;i<=n;i++)
if(va[i]&&!vb[i])
{
res++;
for(int j=1;j<=n;j++)
if(vb[j]&&!va[j]&&m[i][j])g[i-1][j-1]=1;
}
vN=uN=n;
if(hungary()!=res)printf("NO\n");
else
{
printf("YES\n");
ans.clear();
S.clear();
for(int i=1;i<=n;i++)
if(vb[i])
{
if(va[i])continue;
int u=linker[i-1]+1,v=i;
memset(vis,0,sizeof(vis));
dfs(u,v);
va[u]=0,va[v]=1;
}
printf("%d\n",ans.size());
for(int i=0;i<ans.size();i++)printf("%d %d\n",ans[i].first,ans[i].second,i==ans.size()-1?'\n':' ');
}
}
return 0;
}