学校放假了.....有些同窗回家了,而有些同窗则有之前的好朋友来探访,那么住宿就是一个问题。好比A和B都是学校的学生,A要回家,而C来看B,C与A不认识。咱们假设每一个人只能睡♂和本身直接认识的人(的床)。那么一个解决方案就是B睡A(的床)而C睡B(的床)。而实际状况可能很是复杂,有的人可能认识好多在校学生,在校学生之间也不必定都互相认识。咱们已知一共有\(n\)我的,而且知道其中每一个人是否是本校学生,也知道每一个本校学生是否回家。问是否存在一个方案使得全部不回家的本校学生和来看他们的其余人都有地方住。c++
第一行一个数\(T\)表示数据组数。接下来\(T\)组数据,每组数据第一行一个数\(n\)表示涉及到的总人数。接下来一行\(n\)个数,第\(i\)个数表示第\(i\)我的是不是在校学生(0表示不是,1表示是)。再接下来一行\(n\)个数,第\(i\)个数表示第\(i\)我的是否回家(0表示不回家,1表示回家,注意若是第\(i\)我的不是在校学生,那么这个位置上的数是一个随机的数,你应该在读入之后忽略它)。接下来\(n\)行每行\(n\)个数,第\(i\)行第\(j\)个数表示\(i\)和\(j\)是否定识(1表示认识,0表示不认识,第\(i\)行第\(i\)个值为0,可是显然本身仍是能够睡本身的床),认识的关系是相互的。算法
对于每组数据,若是存在一个方案,则输出"^_^"(不含引号)不然输出"T_T"(不含引号)。(注意输出的都是半角字符,即三个符号的 ASCII 码分别为94,84,95)。spa
1 3 1 1 0 0 1 0 0 1 1 1 0 0 1 0 0
^_^
对于\(30\%\)的数据知足\(1 \leq n \leq 12\).code
对于\(100\%\)的数据知足\(1 \leq n \leq 50,1\le T\le 20\).get
这个问题中咱们把\(n\)我的看做集合\(A\),把\(n\)张床看做集合\(B\)(但显然非本校学生没有床位)。若是对第\(i\)我的和第\(j\)张床,它们之间知足如下关系:it
1.\(i\)不是本校学生,或者\(i\)是本校学生且假期不回家。class
2.\(j\)和\(i\)互相认识(包括\(j=i\)的状况),且\(j\)是本校学生(不然没有床位)。搜索
则在\(A_i,B_j\)之间链接一条边:数据
这样咱们就构造了一张二部图\(G=<A,B,E>\)。很显然,求解是否存在一种使得全部不回家的本校学生和来看他们的校外人都有地方住,就至关于求此二部图的最大匹配边数可否知足所需床位数。采用dfs+匈牙利算法求解。集合
#include <bits/stdc++.h> using namespace std; int T, n; int Link[60][60] = {}, matched[60] = {}, isStudent[60] = {}, goesHome[60] = {}; int flgz; bool visited[100] = {}; inline int readNum() { int x = 0; char ch = getchar(); while (ch < '0' || ch > '9') ch = getchar(); while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); } return x; } bool dfs(int x) { for (int j = 1; j <= Link[x][0]; j++) { //寻找第x我的可链接的边,这样的边一共有Link[x][0]条 if (!visited[Link[x][j]] && isStudent[Link[x][j]]) { //!visited的含义是:对于第x我的,他链接的第j条边没有被匹配过 visited[Link[x][j]] = true; if (!matched[Link[x][j]] || dfs(matched[Link[x][j]])) { //若是第j床没有被匹配,或向下搜索找到增广交错路径 matched[Link[x][j]] = x; //就将x的第j条边链接的床标号为x return true; } } } return false; } int main() { T = readNum(); while (T--) { int rbq = 0; memset(Link, 0, sizeof(Link)); memset(matched, 0, sizeof(p)); memset(isStudent, 0, sizeof(isStudent)); memset(goesHome, 0, sizeof(goesHome)); n = readNum(); for (int i = 1; i <= n; i++) isStudent[i] = readNum(); for (int i = 1; i <= n; i++) { rbq = readNum(); if (isStudent[i]) goesHome[i] = rbq; else goesHome[i] = -1; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { rbq = readNum(); if (goesHome[i] == 1) continue; if (rbq) { //若是i和j认识,i能够用j的床 Link[i][0]++; //[0]表示第i我的当前链接的边数,下记做ki Link[i][Link[i][0]] = j; //若是ij认识,把i链接的第ki条边标记上j } if (i == j && isStudent[i] == 1) { Link[i][0]++; Link[i][Link[i][0]] = j; //若是第i我的是学生,将其和本身的床连一条边 } } } flgz = 1; for (int i = 1; i <= n; i++) { if (goesHome[i] != 1) { memset(visited, 0, sizeof(visited)); if (!dfs(i)) { printf("T_T\n"); flgz = 0; break; } } } if (flgz) printf("^_^\n"); } }