http://47.99.179.148/problem.php?id=1038
import java.util.*;
/**
* @author SJ
* @date 2020/12/2
*/
public class MaxFlow {
public static Scanner scanner = new Scanner(System.in);
/**
* 第一行输入T(T<=10)表示有T组数据。每组数据先输入两个正整数 n,m (n,m<=50),接下来n行每行先输入一个k,代表第i号男嘉宾中意的女嘉宾人数(k<=10),随后
* 输入k个数用空格分开,代表这名男嘉宾中意的女嘉宾编号名单。
*/
public static void main(String[] args) {
int T = scanner.nextInt();
for (int i = 0; i < T; i++) {
// System.out.println("男女嘉宾个数:");
int n = scanner.nextInt(); //n个男嘉宾
int m = scanner.nextInt();//m个女嘉宾
//编号0 表示源点,G(i,j)!=0表示第i号男嘉宾中意第j号女嘉宾,第n+m+1 个编号表示汇点
//源点与所有男生相连,汇点与所有女生相连,目标是找到源点到汇点的最大流
//1到n表示男嘉宾,n到n+m表示女嘉宾
//网络中的节点数量,男+女+源点+汇点
int N = n + m + 2;
int[][] G = new int[N][N];
for (int j = 0; j < n; j++) {
// System.out.println("di"+(j+1)+"个:喜欢的人数");
int k = scanner.nextInt();
for (int p = 0; p < k; p++) {
// System.out.println("她们分别是");
int boyIndex = j + 1;
int girlIndex = scanner.nextInt()+n;
G[boyIndex][girlIndex] = 1;
}
}
for (int j = 1; j < N - 1; j++) {
if (j <= n)
G[0][j] = 1;//源点与男生相连
else
G[j][N - 1] = 1;//汇点与女生相连
}
int maxFlow = maxFlow(G, N);
System.out.println(maxFlow);
}
}
//在网络中找到一条路径并更新网络
public static Boolean findPath(int[][] G, int N) {
int[] temp = new int[N];//存放找到的路径
Arrays.fill(temp,-1);
Set<Integer> visited = new HashSet<>();
Queue<Integer> queue = new LinkedList<>();
queue.add(0);//放入源点
visited.add(0);
s: while (!queue.isEmpty()) {
int size = queue.size();
for (int i = 0; i < size; i++) {
Integer cur = queue.poll();
if (cur == N - 1)//找到终点
{
//修改残留网络;
for (int q = N - 1; q >0; ) {
int x = temp[q];
G[x][q] = 0;
G[q][x] = 1;
q = temp[q];
}
return true;
}
for (int j = 0; j < N; j++) {
//有边相连且没访问过
if (G[cur][j] != 0 && !visited.contains(j)) {
queue.offer(j);
visited.add(j);
temp[j] = cur;
}
}
}
}
return false;
}
public static int maxFlow(int[][] G,int N){
int MaxFlow=0;
Boolean flag=findPath(G,N);
while (flag){
flag=findPath(G,N);
MaxFlow++;
}
return MaxFlow;
}
}
// MaxFlow.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//
#include <iostream>
#include <queue>
#include <set>
#include<list>
using namespace std;
#define maxn 102
int m, n;//m位女嘉宾,n位男嘉宾
int N ;
int a[maxn][maxn];//存放女嘉宾价格表
int G[maxn][maxn];//存放网络图
int Pre[maxn];//存放当前找到的从源点到汇点的路径
int cost[maxn];//存放源点到该点的最短路径
set<int> visited;//定义访问过的对象
//初始化
void initialize() {
//初始化存放的路径,每次寻找路径时都要用
for (int i = 0; i < N; i++) {
Pre[i] = -1;
if (i==0)
{
cost[i] = 0;
}
else
{
cost[i] = 999999;
}
}
visited.clear();
}
int find(set<int> myset, int i) {
int flag1 = -1;
for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it) {
if (*it == i)
{
flag1 = 1;
}
}
return flag1;
}
//更新残差网络
void updateG() {
//更新残差网络和价格表
for (int q = N - 1; q > 0; ) {
int x = Pre[q];
G[x][q] = 0;
G[q][x] = 1;
//a[q][x] = 0 - a[x][q];
q = x;
}
}
//找到一条代价最小的路径并更新G
int findPath() {
initialize();
int flag = -1;
list<int> toBevisited;
toBevisited.push_back(0);
visited.insert(0);
while (!toBevisited.empty())
{
int size = toBevisited.size();
int cur = toBevisited.front();
if (cur == N - 1)
{
//可以找到一条路径
flag = 1;
}
toBevisited.pop_front();
visited.erase(cur);
for (int i = 0; i < N; i++) {
if (G[cur][i] != 0)
{
//toBevisited.push_back(i);
//如果当前的耗费较小,就更新
if (cost[cur] + a[cur][i] < cost[i])
{
Pre[i] = cur;
cost[i] = cost[cur] + a[cur][i];
int flag1 = 0;
//for (std::list<int>::iterator it = toBevisited.begin(); it != toBevisited.end(); ++it) {
//if (*it == i)
//{
//flag1 = 1;
//}
//}
if (visited.find(i)==visited.end())
{
toBevisited.push_back(i);
visited.insert(i);
}
}
}
}
}
updateG();
return flag;
}
int main()
{
//第一行输入T(T<=10)表示有T组数据。每组数据先输入两个正整数 n,m (n,m<=50),紧接着输入m行每行n个数a[i][j],用空格隔开,代表i号女嘉宾如果和j号男嘉
//宾牵手需要收取的费用。接下来输入n行每行先输入一个k,代表第i号男嘉宾中意的女嘉宾人数(k <= 10),随后输入k个数用空格分开,代表这名男嘉宾中意的女嘉宾
// 编号名单。
int T;
cin >> T;
while (T--) {
cin >> n >> m;
N = n + m + 2;
for (int i = 0; i < maxn; ++i) {
for (int j = 0; j < maxn; ++j) {
G[i][j] = 0;
a[i][j] = 0;
}
}
for (int i = 1; i <= m; i++)
for (int j = 1; j <= n; j++)
{
int t;
cin >> t;
a[j][i+n]=t;
a[i + n][j] = -t;
}
//初始化G
for (int i = 0; i < N; i++)
for (int j = 0; j < N; j++)
G[i][j] = 0;
//根据初始条件构建G
for (int i = 1; i <= n; i++) {
int k;
cin >> k;
for (int j = 0; j < k; j++) {
int boyIndex = i;
int temp;
cin >> temp;
int girlIndex = temp + n;
G[boyIndex][girlIndex] = 1;
}
}
for (int j = 1; j < N-1; j++) {
if (j <= n)
G[0][j] = 1;//源点与男生相连
else
G[j][N - 1] = 1;//汇点与女生相连
}
int maxFlow = 0;
int minCost = 0;
int m = findPath();
while (m!=-1)
{
maxFlow++;
minCost += cost[N - 1];
m = findPath();
}
cout << maxFlow << " " << minCost<<endl;
}
return 0;
}
/*
3
2 2
3 5
4 2
1 1
2 1 2
3 3
2 6 1
3 5 4
1 7 1
2 1 2
1 3
2 1 2
2 2
3 5
4 2
1 1
2 1 2
*/
1.建立网络(心动网络和费用表)
2.在网络中寻找一条从源点到汇点且花费最小的路径来更新网络(该条路径上的所有边反向)
3.直到图中再也找不到一条源点到汇点的路径。