挑战程序竞赛系列(27):3.5二分图匹配(2)
详细代码可以fork下Github上leetcode项目,不定期更新。
练习题如下:
- POJ 1466: Girls and Boys
- POJ 3692: Kindergarten
- POJ 2724: Purifying Machine
- POJ 2226: Muddy Fields
- POJ 2251: Merry Christmas
POJ 1466: Girls and Boys
活生生的拆散情侣啊,求最大匹配数,接着一刀切,所以答案就是所有学生数减去最大匹配数,当然需要注意addEdge方法,只需要添加一条边即可。因为题目假设情侣们都是两情相悦滴。
代码如下:
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;
public class Main{
InputStream is;
PrintWriter out;
String INPUT = "./data/judge/201707/1466.txt";
void solve() {
while (in.hasNext()){
int n = in.nextInt();
init(n);
for (int i = 0; i < n; ++i){
in.next();
String str = in.next();
int num = Integer.parseInt(str.substring(1, str.length() - 1));
for (int j = 0; j < num; ++j){
int id = in.nextInt();
addEdge(i, id);
}
}
int pairs = bipartiteMatching();
System.out.println(n - pairs);
}
in.close();
}
//二分图
List<Integer>[] g;
int[] matching;
int V;
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
Scanner in;
public Main(){
in = new Scanner(System.in);
}
void run() throws Exception {
solve();
}
public static void main(String[] args) throws Exception {
new Main().run();
}
}
当然你也可以参考《挑战》P221关于匹配,边覆盖,独立集和顶点覆盖的概念和公式,简单说说。
上述概念都针对一般图,于是有:
(a) 对于不存在孤立点的图,|最大匹配| + |最小边覆盖| = |V|
(b) |最大独立集| + |最小顶点覆盖| = |V|
问题a,求出最大匹配即能求出最小边覆盖,问题b中,针对一般图,求解最大独立集和最小顶点覆盖是NP困难的,但是在二分图中有:
(c) |最大匹配| = |最小顶点覆盖|
所以二分图中能够快速求出最大独立集。
POJ 3692: Kindergarten
思路:可以转换为求补图的最大独立集,而补图恰好是个二分图。二分图的最大独立集 = 总点数 - 二分图最大匹配。于是问题就转换成了求补图的最大匹配了。
证明:补图中的最大独立集,彼此没有边相连,反过来就是彼此都互相连接。
代码如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;
public class Main{
InputStream is;
PrintWriter out;
String INPUT = "./data/judge/201707/3692.txt";
void solve() {
int cnt = 0;
while (true){
int G = ni();
int B = ni();
int M = ni();
if (G + B + M == 0) break;
init(G + B);
boolean[][] gg = new boolean[G][B];
for (int i = 0; i < M; ++i){
int g = ni();
int b = ni();
g --;
b --;
gg[g][b] = true;
}
for (int i = 0; i < G; ++i){
for (int j = 0; j < B; ++j){
if (!gg[i][j]) addEdge(i, j + G);
}
}
out.println("Case " + (++cnt) + ": " + (G + B - bipartiteMatching()));
}
}
//二分图
List<Integer>[] g;
int V;
int[] matching;
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
g[to].add(from);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
void run() throws Exception {
is = oj ? System.in : new FileInputStream(new File(INPUT));
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
tr(System.currentTimeMillis() - s + "ms");
}
public static void main(String[] args) throws Exception {
new Main().run();
}
private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0;
private int readByte() {
if (lenbuf == -1)
throw new InputMismatchException();
if (ptrbuf >= lenbuf) {
ptrbuf = 0;
try {
lenbuf = is.read(inbuf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (lenbuf <= 0)
return -1;
}
return inbuf[ptrbuf++];
}
private boolean isSpaceChar(int c) {
return !(c >= 33 && c <= 126);
}
private int skip() {
int b;
while ((b = readByte()) != -1 && isSpaceChar(b))
;
return b;
}
private double nd() {
return Double.parseDouble(ns());
}
private char nc() {
return (char) skip();
}
private String ns() {
int b = skip();
StringBuilder sb = new StringBuilder();
while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != '
// ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
private char[] ns(int n) {
char[] buf = new char[n];
int b = skip(), p = 0;
while (p < n && !(isSpaceChar(b))) {
buf[p++] = (char) b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
}
private char[][] nm(int n, int m) {
char[][] map = new char[n][];
for (int i = 0; i < n; i++)
map[i] = ns(m);
return map;
}
private int[] na(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = ni();
return a;
}
private int ni() {
int num = 0, b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private long nl() {
long num = 0;
int b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
private void tr(Object... o) {
if (!oj)
System.out.println(Arrays.deepToString(o));
}
}
POJ 2724: Purifying Machine
题意可参考博文:http://www.hankcs.com/program/algorithm/poj-2724-purifying-machine.html
这台净化机是可以编程的,意味着:
3 3
*01
100
011
0 0
会有:
101,001,100,011
编程:
10* (101, 100)合并而成
0*1 (001, 011)合并而成
所以最少操作为2次
那么无非就是尽可能的多构造*
,首先把含*
的构造成0or1,接着编辑距离为1的两个num建立连接,因为它们可能构成新的*
,从而降低操作次数,二分图匹配求最大独立集。
代码如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Set;
public class Main{
InputStream is;
PrintWriter out;
String INPUT = "./data/judge/201707/2724.txt";
void solve() {
while (true){
int N = ni();
int M = ni();
if (N + M == 0) break;
Set<Integer> set = new HashSet<Integer>();
for (int i = 0; i < M; ++i){
String line = ns();
if (line.contains("*")){
int a1 = 0;
int a2 = 0;
for (char c : line.toCharArray()){
if (c != '*'){
a1 |= c - '0';
a2 |= c - '0';
}
else{
a1 |= 0;
a2 |= 1;
}
a1 <<= 1;
a2 <<= 1;
}
set.add(a1 >> 1);
set.add(a2 >> 1);
}
else{
int num = 0;
for (char c : line.toCharArray()){
num |= c - '0';
num <<= 1;
}
set.add(num >> 1);
}
}
Set<Integer> dict = new HashSet<Integer>();
for (int i = 0, num = 1; i < N; ++i){
dict.add(num);
num <<= 1;
}
List<Integer> operations = new ArrayList<Integer>(set);
init(set.size());
for (int i = 0; i < V; ++i){
for (int j = i + 1; j < V; ++j){
int diff = operations.get(i) ^ operations.get(j);
if (dict.contains(diff)){
addEdge(i, j);
}
}
}
out.println(V - bipartiteMatching());
}
}
//二分图
List<Integer>[] g;
int V;
int[] matching;
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
g[to].add(from);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
void run() throws Exception {
is = oj ? System.in : new FileInputStream(new File(INPUT));
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
tr(System.currentTimeMillis() - s + "ms");
}
public static void main(String[] args) throws Exception {
new Main().run();
}
private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0;
private int readByte() {
if (lenbuf == -1)
throw new InputMismatchException();
if (ptrbuf >= lenbuf) {
ptrbuf = 0;
try {
lenbuf = is.read(inbuf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (lenbuf <= 0)
return -1;
}
return inbuf[ptrbuf++];
}
private boolean isSpaceChar(int c) {
return !(c >= 33 && c <= 126);
}
private int skip() {
int b;
while ((b = readByte()) != -1 && isSpaceChar(b))
;
return b;
}
private double nd() {
return Double.parseDouble(ns());
}
private char nc() {
return (char) skip();
}
private String ns() {
int b = skip();
StringBuilder sb = new StringBuilder();
while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != '
// ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
private char[] ns(int n) {
char[] buf = new char[n];
int b = skip(), p = 0;
while (p < n && !(isSpaceChar(b))) {
buf[p++] = (char) b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
}
private char[][] nm(int n, int m) {
char[][] map = new char[n][];
for (int i = 0; i < n; i++)
map[i] = ns(m);
return map;
}
private int[] na(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = ni();
return a;
}
private int ni() {
int num = 0, b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private long nl() {
long num = 0;
int b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
private void tr(Object... o) {
if (!oj)
System.out.println(Arrays.deepToString(o));
}
}
关于位的操作还不够完美,继续优化。
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.InputMismatchException;
import java.util.List;
import java.util.Set;
public class Main{
InputStream is;
PrintWriter out;
String INPUT = "./data/judge/201707/2724.txt";
void solve() {
while (true){
int N = ni();
int M = ni();
if (N + M == 0) break;
Set<Integer> set = new HashSet<Integer>();
for (int i = 0; i < M; ++i){
String line = ns();
if (line.contains("*")){
int a1 = 0;
int a2 = 0;
for (char c : line.toCharArray()){
a1 <<= 1;
a2 <<= 1;
if (c != '*'){
a1 |= c - '0';
a2 |= c - '0';
}
else{
a1 |= 0;
a2 |= 1;
}
}
set.add(a1);
set.add(a2);
}
else{
int num = 0;
for (char c : line.toCharArray()){
num <<= 1;
num |= c - '0';
}
set.add(num);
}
}
List<Integer> operations = new ArrayList<Integer>(set);
init(set.size());
for (int i = 0; i < V; ++i){
for (int j = i + 1; j < V; ++j){
int diff = operations.get(i) ^ operations.get(j);
if ((diff & (-diff)) == diff){
addEdge(i, j);
}
}
}
out.println(V - bipartiteMatching());
}
}
//二分图
List<Integer>[] g;
int V;
int[] matching;
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
g[to].add(from);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
void run() throws Exception {
is = oj ? System.in : new FileInputStream(new File(INPUT));
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
tr(System.currentTimeMillis() - s + "ms");
}
public static void main(String[] args) throws Exception {
new Main().run();
}
private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0;
private int readByte() {
if (lenbuf == -1)
throw new InputMismatchException();
if (ptrbuf >= lenbuf) {
ptrbuf = 0;
try {
lenbuf = is.read(inbuf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (lenbuf <= 0)
return -1;
}
return inbuf[ptrbuf++];
}
private boolean isSpaceChar(int c) {
return !(c >= 33 && c <= 126);
}
private int skip() {
int b;
while ((b = readByte()) != -1 && isSpaceChar(b))
;
return b;
}
private double nd() {
return Double.parseDouble(ns());
}
private char nc() {
return (char) skip();
}
private String ns() {
int b = skip();
StringBuilder sb = new StringBuilder();
while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != '
// ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
private char[] ns(int n) {
char[] buf = new char[n];
int b = skip(), p = 0;
while (p < n && !(isSpaceChar(b))) {
buf[p++] = (char) b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
}
private char[][] nm(int n, int m) {
char[][] map = new char[n][];
for (int i = 0; i < n; i++)
map[i] = ns(m);
return map;
}
private int[] na(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = ni();
return a;
}
private int ni() {
int num = 0, b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private long nl() {
long num = 0;
int b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
private void tr(Object... o) {
if (!oj)
System.out.println(Arrays.deepToString(o));
}
}
对于非零的整数,x & (-x)的值就是将其最低位的1独立出来后的值。具体可以参考《挑战》P157.
POJ 2226: Muddy Fields
思路:二分图最小顶点集等于最大匹配数。此二分匹配一定能覆盖所有沼泽,且一个很重要的条件是宽度为1任意长的木板,所以只需要找做左顶点和上顶点构成的木板即可,接着就是问题转换了。
最少木板数,可以理解为被覆盖的沼泽最少,尽可能不去覆盖这样木板数一定是最少的,可现实总是很残酷,总有那么一些沼泽要披上两层木板,此时拿个木钉把横竖木板钉在一块,所使用的钉子最少,就是所求答案。
代码如下:
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.InputMismatchException;
import java.util.List;
public class Main{
InputStream is;
PrintWriter out;
String INPUT = "./data/judge/201707/2226.txt";
void solve() {
int R = ni();
int C = ni();
char[][] board = new char[R][C];
for (int i = 0; i < R; ++i){
for (int j = 0; j < C; ++j){
board[i][j] = nc();
}
}
init(5000 + 16);
for (int i = 0; i < R; ++i){
for (int j = 0; j < C; ++j){
if (board[i][j] == '*'){
int x = i, y = j;
while (x > 0 && board[x - 1][j] == '*') x--;
while (y > 0 && board[i][y - 1] == '*') y--;
addEdge(x * 50 + j, i * 50 + y + 2500);
}
}
}
out.println(bipartiteMatching());
}
//二分图最大匹配等价于最小顶点覆盖
List<Integer>[] g;
int V;
int[] matching;
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
g[to].add(from);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
void run() throws Exception {
is = oj ? System.in : new FileInputStream(new File(INPUT));
out = new PrintWriter(System.out);
long s = System.currentTimeMillis();
solve();
out.flush();
tr(System.currentTimeMillis() - s + "ms");
}
public static void main(String[] args) throws Exception {
new Main().run();
}
private byte[] inbuf = new byte[1024];
public int lenbuf = 0, ptrbuf = 0;
private int readByte() {
if (lenbuf == -1)
throw new InputMismatchException();
if (ptrbuf >= lenbuf) {
ptrbuf = 0;
try {
lenbuf = is.read(inbuf);
} catch (IOException e) {
throw new InputMismatchException();
}
if (lenbuf <= 0)
return -1;
}
return inbuf[ptrbuf++];
}
private boolean isSpaceChar(int c) {
return !(c >= 33 && c <= 126);
}
private int skip() {
int b;
while ((b = readByte()) != -1 && isSpaceChar(b))
;
return b;
}
private double nd() {
return Double.parseDouble(ns());
}
private char nc() {
return (char) skip();
}
private String ns() {
int b = skip();
StringBuilder sb = new StringBuilder();
while (!(isSpaceChar(b))) { // when nextLine, (isSpaceChar(b) && b != '
// ')
sb.appendCodePoint(b);
b = readByte();
}
return sb.toString();
}
private char[] ns(int n) {
char[] buf = new char[n];
int b = skip(), p = 0;
while (p < n && !(isSpaceChar(b))) {
buf[p++] = (char) b;
b = readByte();
}
return n == p ? buf : Arrays.copyOf(buf, p);
}
private char[][] nm(int n, int m) {
char[][] map = new char[n][];
for (int i = 0; i < n; i++)
map[i] = ns(m);
return map;
}
private int[] na(int n) {
int[] a = new int[n];
for (int i = 0; i < n; i++)
a[i] = ni();
return a;
}
private int ni() {
int num = 0, b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private long nl() {
long num = 0;
int b;
boolean minus = false;
while ((b = readByte()) != -1 && !((b >= '0' && b <= '9') || b == '-'))
;
if (b == '-') {
minus = true;
b = readByte();
}
while (true) {
if (b >= '0' && b <= '9') {
num = num * 10 + (b - '0');
} else {
return minus ? -num : num;
}
b = readByte();
}
}
private boolean oj = System.getProperty("ONLINE_JUDGE") != null;
private void tr(Object... o) {
if (!oj)
System.out.println(Arrays.deepToString(o));
}
}
在构图时,遍历当前board是否为”*”,接着把它们定位到最上端和最左端的两块木板,并建立边,形成二分图。
POJ 2251: Merry Christmas
思路:假设L个地方需要L个圣诞老人,那么现在就在此基础上尽可能多的节约圣诞老人。所以说假如知道某个地点的圣诞老人出现的时刻,那么在规定时间内能够抵达另一个地方,那么可以省去一个圣诞老人。最多可以省去多少呢?为最大匹配数,这样此题就转换成了二分图的求解。
代码如下:
static final int INF = 1 << 28;
void solve() {
int[][] distance = new int[100][100];
while (true){
int N = ni();
int M = ni();
int L = ni();
if(N == 0 && M == 0 && L == 0) break;
for (int i = 0; i < 100; ++i){
Arrays.fill(distance[i], INF);
}
for (int i = 0; i < M; ++i){
int from = ni();
int to = ni();
int dis = ni();
distance[from][to] = dis;
distance[to][from] = dis;
}
for (int k = 0; k < N; ++k){
distance[k][k] = 0;
for (int i = 0; i < N; ++i){
for (int j = 0; j < N; ++j){
distance[i][j] = Math.min(distance[i][j], distance[i][k] + distance[k][j]);
}
}
}
int[] p = new int[L];
int[] t = new int[L];
for (int i = 0; i < L; ++i){
p[i] = ni();
t[i] = ni();
}
init(2 * L);
for (int i = 0; i < L; ++i){
for (int j = 0; j < L; ++j){
if (i != j && t[i] + distance[p[i]][p[j]] <= t[j]){
addEdge(2 * i, 2 * j + 1);
}
}
}
out.println(L - bipartiteMatching());
}
}
List<Integer>[] g;
int V;
int[] matching;
@SuppressWarnings("unchecked")
public void init(int n){
V = n;
g = new ArrayList[V];
for (int i = 0; i < V; ++i) g[i] = new ArrayList<Integer>();
matching = new int[V];
}
public void addEdge(int from, int to){
g[from].add(to);
g[to].add(from);
}
public boolean dfs(int v, boolean[] visited){
visited[v] = true;
for (int u : g[v]){
int w = matching[u];
if (w == -1 || !visited[w] && dfs(w, visited)){
matching[u] = v;
matching[v] = u;
return true;
}
}
return false;
}
public int bipartiteMatching(){
int res = 0;
Arrays.fill(matching, -1);
for (int i = 0; i < V; ++i){
if (matching[i] < 0){
if (dfs(i, new boolean[V])){
res ++;
}
}
}
return res;
}
很奇怪在全数据集上得到的结果都正确,但在AOJ上跑就Runtime Error,有点气人,欢迎找错。
正确AC代码参考:http://www.hankcs.com/program/algorithm/aoj-2251-merry-christmas.html