Problem:
F: 風呂がオーバーフロー - Overflow of Furo -

物語

温泉宿・パロは自慢の温泉に全ての情熱を注いでおり、風呂のプロフェッショナルを集めている。
風呂のプロたちは主に温泉の配管を管理しており、複数の源泉から1つの大浴場へと繋がる、複雑に入り組んだ配管網を管理・調整している。

配管網の管理・調整業務もなかなかに大変なのだが、風呂のプロたちはその合間を縫って、さらに多くの湯を浴槽へと供給できるよう日々努力を積み重ねていた。
結果、風呂のプロたちは「1本だけ配管をオーバーフローさせることができる」という荒技を習得した。
すなわち、自由に配管1本を選び、その配管の湯量の制限をなくすことができるようになったのだ。

今まで最大湯量を実現する配管網の設定をあなたのプログラムに依存していた風呂のプロたちは、この技術を使ってさらに浴槽への供給湯量を増やすにはどうすればよいか、あなたに再びプログラムを書くよう依頼してきた。

問題

1つの浴槽、K個の源泉、N個の結合点を含む配管網がある。
配管網はM本の配管からなり、配管1つ1つは流すことのできる湯量の制限を持つ。
配管それぞれは湯を流す方向が決められていないので、自由に決めて使ってよい。
N個の結合点では何本かの配管から流れてきた湯をそれ以外の何本かの配管へと自由な配分で流すことができる。
すべての源泉、および浴槽は一部の配管の端点になっており、源泉からの湯量、および結合点での湯量を調整することで源泉から浴槽へと湯を供給している。

M個の配管のうち1本だけオーバーフローさせる、すなわち流せる湯量を無限に増やすことで浴槽に供給できるようになる最大湯量はいくらか。
ただし、最大供給湯量を無限に増やすことができる場合も考えられるが、その場合は風呂がオーバーフローしてしまうので、"overfuro"と出力すること。

入力形式

入力は以下の形式で与えられる。

K N M
a_1 b_1 c_1
...
a_M b_M c_M

入力は全て整数からなる。
最初の行では源泉の数K、結合点の数N、配管の数Mが与えられる。
続くM行のうちi行目には、i番目の配管の情報を表す3つの整数 a_i, b_i, c_i が与えられる。
これはi番目の配管の両端点がそれぞれa_iとb_iであり、湯量の制限がc_iであることを示す。
ここで、配管の端点 x が0のときは大浴場、1からKまでのときは x 番目の源泉、K+1からK+Nまでのときは x − K 番目の結合点であることを表す。

制約

1 ≤ K

0 ≤ N

N+K ≤ 100

1 ≤ M ≤ (N+K+1)(N+K)/2

0 ≤ a_i, b_i ≤ K+N

a_i ≠ b_i

1 ≤ c_i ≤ 5{,}000

同じ2端点を持つ配管は2つ以上存在しないことが保証される。

与えられる配管網は、配管をオーバーフローさせることなく最低1以上の湯を源泉から浴槽に供給できることが保証される。

出力形式

1本だけ配管をオーバーフローさせて源泉から浴槽への供給湯量を最大化するときの最大供給湯量を1行に出力せよ。
ただし、最大湯量を無限に増やせるときは、"overfuro"と1行に出力せよ。

入力例1

2 2 4
1 3 4
2 4 2
0 3 3
4 0 5

出力例1

8

入力例2

2 3 7
1 0 8
2 0 9
3 0 3
0 4 5
5 0 2
1 3 2
2 4 9

出力例2

overfuro

入力例3

1 1 2
0 2 1
1 2 1

出力例3

1

入力例4

5 0 5
0 1 1
0 2 1
0 3 1
0 4 1
0 5 1

出力例4

overfuro

Solution in JAVA (judge verdict: Time Limit Exceeded)


import java.io.*;
import java.util.*;

 
public class Main{
	
	int K;
	int N;
	int M;
	
	public void solve(){
		K = nextInt();
		N = nextInt();
		M = nextInt();
		
		Node[] nodes = new Node[N + K + 2];
		for(int i = 0; i < nodes.length; i++){
			nodes[i] = new Node(i);
		}
		Node sink = nodes[0];
		Node source = nodes[nodes.length - 1];
		for(int i = 0; i < K; i++){
			source.addPath(nodes[1 + i], Integer.MAX_VALUE);
		}
		for(int i = 0; i < M; i++){
			int a = nextInt();
			int b = nextInt();
			int c = nextInt();
			if((1 <= a && a <= K && b == 0) || (1 <= b && b <= K && a == 0)){
				out.println("overfuro");
				return;
			}
			nodes[a].addPath(nodes[b], c);
			nodes[b].addPath(nodes[a], c);
		}
		int base = fordFulkerson(source, sink);
		int ans = 0;
		for(int i = 0; i < nodes.length; i++){
			for(Path p : nodes[i].next){
				if(p.weight == p.value){
					Node[] copy = new Node[nodes.length];
					for(int j = 0; j < nodes.length; j++){
						copy[j] = new Node(j);
					}
					for(Node nn : nodes){
						for(Path pp : nn.next){
							Path np = new Path(copy[nn.idx], copy[pp.to.idx], pp.weight);
							np.value = pp.value;
							if(pp == p){
								np.weight = Integer.MAX_VALUE;
							}
							np.from.next.add(np);
							np.to.rev.add(np);
						}
					}
					ans = Math.max(ans, fordFulkerson(copy[nodes.length - 1], copy[0]));
				}
			}
		}
		out.println(base + ans);
	}
	


	class Node{
		int idx;
		int count;
		List<Path> next;
		List<Path> rev;
		public Node(int idx){
			this.idx = idx;
			this.count = 0;
			next = new LinkedList<>();
			rev = new LinkedList<>();
		}
		public void addPath(Node to, int weight){
			Path p = new Path(this, to, weight);
			next.add(p);
			to.rev.add(p);
		}
	}
	
	class Path{
		Node from;
		Node to;
		int weight;
		int value;
		
		public Path(Node from, Node to, int weight){
			this.from = from;
			this.to = to;
			this.weight = weight;
		}
	}
	
	public int fordFulkerson(Node source, Node sink){
		int count = 1;
		int ans = 0;
		while(true){
			int v = dfs(source, sink, Integer.MAX_VALUE, count);
			if(v == 0) break;
			ans += v;
			count++;
		}
		return ans;
	}
	
	public int dfs(Node current, Node sink, int v, int count){
		if(current.count < count){
			current.count = count;
			
			if(current == sink){
				return v;
				
			}else{
				for(Path next : current.next){
					if(next.weight - next.value > 0){
						int v2 = Math.min(v, next.weight - next.value);
						int v3 = dfs(next.to, sink, v2, count);
						if(v3 > 0){
							next.value += v3;
							return v3;
						}
					}
				}

				for(Path rev : current.rev){
					if(rev.value > 0){
						int v2 = Math.min(v, rev.value);
						int v3 = dfs(rev.from, sink, v2, count);
						if(v3 > 0){
							rev.value -= v3;
							return v3;
						}
					}
				}
			}
		}
		return 0;
	}
	
	private static PrintWriter out;
	public static void main(String[] args){
		out = new PrintWriter(System.out);
		new Main().solve();
		out.flush();
	}
	
	
	
	public static int nextInt(){
		int num = 0;
		String str = next();
		boolean minus = false;
		int i = 0;
		if(str.charAt(0) == '-'){
			minus = true;
			i++;
		}
		int len = str.length();
		for(;i < len; i++){
			char c = str.charAt(i);
			if(!('0' <= c && c <= '9')) throw new RuntimeException();
			num = num * 10 + (c - '0');
		}
		return minus ? -num : num;
	}
	
	public static long nextLong(){
		long num = 0;
		String str = next();
		boolean minus = false;
		int i = 0;
		if(str.charAt(0) == '-'){
			minus = true;
			i++;
		}
		int len = str.length();
		for(;i < len; i++){
			char c = str.charAt(i);
			if(!('0' <= c && c <= '9')) throw new RuntimeException();
			num = num * 10l + (c - '0');
		}
		return minus ? -num : num;
	}
	public static String next(){
		int c;
		while(!isAlNum(c = read())){}
		StringBuilder build = new StringBuilder();
		build.append((char)c);
		while(isAlNum(c = read())){
			build.append((char)c);
		}
		return build.toString();
	}
	
	
	private static byte[] inputBuffer = new byte[1024];
	private static int bufferLength = 0;
	private static int bufferIndex = 0;
	private static int read(){
		if(bufferLength < 0) throw new RuntimeException();
		if(bufferIndex >= bufferLength){
			try{
				bufferLength = System.in.read(inputBuffer);
				bufferIndex = 0;
			}catch(IOException e){
				throw new RuntimeException(e);
			}
			if(bufferLength <= 0) return (bufferLength = -1);
		}
		return inputBuffer[bufferIndex++];
	}
	
	private static boolean isAlNum(int c){
		return '!' <= c && c <= '~';
	}
}