Evanyou Blog 彩带

  题目传送门

  分析:看到这题呢,首先想到的就是搜索,数据范围也不大嘛。但是仔细思考发现这题用搜索很难做,看了大佬们的题解后学到了,这一类题目要用二分图匹配来做。可以知道,如果想要的话,每一个子都可以移动到任意位置(当然会对其他子造成影响),我们还可以发现,每一行或每一列有哪些子其实绝对是固定的,只是它们之间的相对顺序会改变。

  瞎扯了这么多,进入正题吧,首先如果a[i][j]是黑子,那么我们想要得到的目标当然是把它移动到a[i][i],那么他当前的位置就可以看作j,目标位置是i,那么就把j和i之间连一条边,将所有边连接以后发现这其实是个二分图,那么就二分图匹配啊,如果能满足有n个匹配那么就说明目标状态可以达到(这个应该不难想吧)。所以建边以后直接匈牙利算法就OK了。(PS:补充一句,建边的时候别忘了把数组开大点,一开始脑抽忘了,结果RE得非常感人......)

  Code:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<iomanip>
#include<algorithm>
const int N=201;
int T,n;
int a[N][N],match[N];
int head[N],size;
bool vis[N];
struct Node{
  int to,next;
}edge[50005];
inline int read()
{
  char ch=getchar();int num=0;bool flag=false;
  while(ch<'0'||ch>'9'){if(ch=='-')flag=true;ch=getchar();}
  while(ch>='0'&&ch<='9'){num=num*10+ch-'0';ch=getchar();}
  return flag?-num:num;
}
inline void ready()
{
  size=0;
  memset(head,-1,sizeof(head));
  memset(match,-1,sizeof(match));
  memset(vis,false,sizeof(vis));
}
inline void add(int x,int y)
{
  edge[++size].to=y;
  edge[size].next=head[x];
  head[x]=size;
}
inline bool dfs(int x)
{
  for(int i=head[x];i!=-1;i=edge[i].next){
    int y=edge[i].to;
    if(vis[y])continue;
    vis[y]=true;
    if(match[y]==-1||dfs(match[y])){
      match[y]=x;
      return true;}}
  return false;
}
inline void work()
{
  n=read();
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      a[i][j]=read();
  for(int i=1;i<=n;i++)
    for(int j=1;j<=n;j++)
      if(a[i][j])add(j,i);
  for(int i=1;i<=n;i++){
    memset(vis,false,sizeof(vis));
    if(!dfs(i)){printf("No
");return;}}
  printf("Yes
");
}
int main()
{
  T=read();
  while(T--){
    ready();
    work();}
  return 0;
}
原文地址:https://www.cnblogs.com/cytus/p/8711095.html