Problem p01706 (C++) — buggy submission, verdict: Time Limit Exceeded

#include <algorithm>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <stdio.h>
#include <string>
#include <time.h>
#include <vector>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

#define NUM 1005

enum Type {
  FROM_S,
  FROM_T,
};

// 辺を表す構造体(行先、容量、逆辺のインデックス)
struct Edge {
  Edge(int arg_to, int arg_capacity, int arg_rev_index, bool arg_is_rev) {
    to = arg_to;
    capacity = arg_capacity;
    rev_index = arg_rev_index;
    is_rev = arg_is_rev;
  }
  int to, capacity, rev_index;
  bool is_rev;
};

int V, E;
int N, M, S, T;

vector<Edge> G[NUM];    // グラフの隣接リスト表現
bool used[NUM];         // DFSですでに調べたかのフラグ
bool can_reach[2][NUM]; // S,Tから各点へ到達できるかのチェック

// fromからtoへ向かう容量capacityの辺をグラフに追加する
void add_edge(int from, int to, int capacity) {
  G[from].push_back(Edge(to, capacity, G[to].size(), false));
  G[to].push_back(
      Edge(from, 0, G[from].size() - 1, true)); // 逆辺の、初期容量は0
}

// 増加パスをDFSで探す
int dfs(int node_id, int sink, int flow) {
  if (node_id == sink)
    return flow; // 終点についたらflowをreturn
  used[node_id] = true;

  for (int i = 0; i < G[node_id].size();
       i++) { // node_idから出ているエッジを調査
    Edge &e = G[node_id][i];
    if (used[e.to] == false &&
        e.capacity >
            0) { // エッジの先のノードに未訪問で、かつエッジに流す余裕がある場合
      int tmp_flow = dfs(e.to, sink, min(flow, e.capacity)); // 流せるだけ流す
      if (tmp_flow > 0) {                                    // 流せた場合
        e.capacity -= tmp_flow; // 流した分、エッジの容量を削減する
        G[e.to][e.rev_index].capacity +=
            tmp_flow; // 逆辺の容量を、流した分だけ増加させる
        return tmp_flow;
      }
    }
  }
  return 0;
}

void reach_check(Type type, int node_id) {

  can_reach[type][node_id] = true;
  int base;

  if (type == FROM_S) {
    base = 1;
  } else {
    base = 0; // ★逆向きに進めれば良いため★
  }

  for (int i = 0; i < G[node_id].size(); i++) {
    if (G[node_id][i].capacity == base &&
        can_reach[type][G[node_id][i].to] == false) {
      reach_check(type, G[node_id][i].to);
    }
  }
}

// sourceからsinkへの最大流を求める
int max_flow(int source, int sink) { // source:始点 sink:終点
  int flow = 0, add;
  while (true) { // 増加パスが存在する限り、流量を追加し続ける
    for (int i = 0; i < V; i++)
      used[i] = false;
    add = dfs(source, sink, BIG_NUM);
    if (add == 0)
      break;
    flow += add;
  }
  return flow;
}

void func() {

  for (int i = 0; i < N; i++) {
    G[i].clear();
    can_reach[FROM_S][i] = false;
    can_reach[FROM_T][i] = false;
  }

  S--;
  T--;

  int from, to;
  for (int loop = 0; loop < M; loop++) {
    scanf("%d %d", &from, &to);
    from--;
    to--;
    add_edge(from, to, 1);
  }

  V = N;

  int FLOW = max_flow(S, T);
  // printf("FLOW:%d\\n",FLOW);
  // return;
  int MAX_FLOW = FLOW;

  reach_check(FROM_S, S);
  reach_check(FROM_T, T);

  int num_edge = 0, diff;

  for (int i = 0; i < N; i++) {
    for (int k = 0; k < G[i].size(); k++) {
      if (G[i][k].is_rev == true || G[i][k].capacity == 0 ||
          can_reach[FROM_S][G[i][k].to] == false ||
          can_reach[FROM_T][i] == false)
        continue; // 逆辺または、最大流達成のために使用した辺はSKIP

      // ★★経路に重なりがあるかも知れないので、仮にcan_reachが両方trueでも、流量が1増えるとは限らない★★

      // 最大流達成の際に使用していない辺のみ、向きを逆転させれば、総経路が1増える可能性がある

      // 辺の向きを逆転させる
      G[i][k].capacity = 0;
      G[G[i][k].to][G[i][k].rev_index].capacity = 1;

      // printf("%d-%dを逆転\\n",i,G[i][k].to);
      // S→T方向に流す
      diff = max_flow(S, T);

      // printf("diff:%d\\n",diff);
      // return;

      if (diff == 1) { // 流量が増加した場合
        num_edge++;
        MAX_FLOW = FLOW + 1;

        if (i != T) {
          max_flow(T, i); // T→iに押し戻す
        }
        if (G[i][k].to != S) {
          max_flow(G[i][k].to, S); // G[i][k].to→Sに押し戻す
        }
      }

      // 辺の向きを元に戻す
      G[i][k].capacity = 1;
      G[G[i][k].to][G[i][k].rev_index].capacity = 0;
    }
  }

  printf("%d %d\\n", MAX_FLOW, num_edge);
}

int main() {

  while (true) {
    scanf("%d %d %d %d", &N, &M, &S, &T);
    if (N == 0 && M == 0 && S == 0 && T == 0)
      break;

    func();
  }

  return 0;
}


Error: TLE

Fixed submission:

#include <algorithm>
#include <cfloat>
#include <cmath>
#include <iostream>
#include <map>
#include <queue>
#include <set>
#include <stack>
#include <stdio.h>
#include <string>
#include <time.h>
#include <vector>
typedef long long int ll;
typedef unsigned long long int ull;
#define BIG_NUM 2000000000
#define MOD 1000000007
#define EPS 0.000000001
using namespace std;

#define NUM 1005

enum Type {
  FROM_S,
  FROM_T,
};

// 辺を表す構造体(行先、容量、逆辺のインデックス)
struct Edge {
  Edge(int arg_to, int arg_capacity, int arg_rev_index, bool arg_is_rev) {
    to = arg_to;
    capacity = arg_capacity;
    rev_index = arg_rev_index;
    is_rev = arg_is_rev;
  }
  int to, capacity, rev_index;
  bool is_rev;
};

int V, E;
int N, M, S, T;

vector<Edge> G[NUM];    // グラフの隣接リスト表現
int dist[NUM];          // sourceからの距離
int cheked_index[NUM];  // どこまで調べ終わったか
bool can_reach[2][NUM]; // S,Tから各点へ到達できるかのチェック

// fromからtoへ向かう容量capacityの辺をグラフに追加する
void add_edge(int from, int to, int capacity) {
  G[from].push_back(Edge(to, capacity, G[to].size(), false));
  G[to].push_back(
      Edge(from, 0, G[from].size() - 1, true)); // 逆辺の、初期容量は0
}

// sourceからの最短距離をBFSで計算する
void bfs(int source) {
  for (int i = 0; i < V; i++)
    dist[i] = -1;
  queue<int> Q;
  dist[source] = 0;
  Q.push(source);

  while (!Q.empty()) {
    int node_id = Q.front();
    Q.pop();
    for (int i = 0; i < G[node_id].size(); i++) {
      Edge &e = G[node_id][i];
      if (e.capacity > 0 &&
          dist[e.to] < 0) { // 辺の容量が正で、かつエッジの行先に未訪問の場合
        dist[e.to] = dist[node_id] + 1;
        Q.push(e.to);
      }
    }
  }
}

// 増加パスをDFSで探す
int dfs(int node_id, int sink, int flow) {
  if (node_id == sink)
    return flow; // 終点についたらflowをreturn

  for (int &i = cheked_index[node_id]; i < G[node_id].size();
       i++) { // node_idから出ているエッジを調査
    Edge &e = G[node_id][i];
    if (e.capacity > 0 &&
        dist[node_id] <
            dist[e.to]) { // 流せる余裕があり、かつsourceからの距離が増加する方法である場合
      int tmp_flow = dfs(e.to, sink, min(flow, e.capacity)); // 流せるだけ流す
      if (tmp_flow > 0) {                                    // 流せた場合
        e.capacity -= tmp_flow; // 流した分、エッジの容量を削減する
        G[e.to][e.rev_index].capacity +=
            tmp_flow; // 逆辺の容量を、流した分だけ増加させる
        return tmp_flow;
      }
    }
  }
  return 0;
}

// sourceからsinkへの最大流を求める
int max_flow(int source, int sink) { // source:始点 sink:終点
  int flow = 0, add;
  while (true) { // 増加パスが存在する限り、流量を追加し続ける
    bfs(source);
    if (dist[sink] < 0)
      break; // sourceからsinkへと辿り着く残余グラフがない、つまり増加パスが無くなった場合、break
    for (int i = 0; i < V; i++)
      cheked_index[i] = 0;
    while ((add = dfs(source, sink, BIG_NUM)) >
           0) { // 増加パスが見つかる間、加算
      flow += add;
    }
  }
  return flow;
}

// あらかじめ、S,Tから行けないノードを特定しておく
void reach_check(Type type, int node_id) {

  can_reach[type][node_id] = true;

  for (int i = 0; i < G[node_id].size(); i++) {
    if (G[node_id][i].capacity == 1 &&
        can_reach[type][G[node_id][i].to] == false) {
      reach_check(type, G[node_id][i].to);
    }
  }
}

void func() {

  for (int i = 0; i < N; i++) {
    G[i].clear();
    can_reach[FROM_S][i] = false;
    can_reach[FROM_T][i] = false;
  }

  S--;
  T--;

  int from, to;
  for (int loop = 0; loop < M; loop++) {
    scanf("%d %d", &from, &to);
    from--;
    to--;
    add_edge(from, to, 1);
  }

  V = N;

  int FLOW = max_flow(S, T);
  int MAX_FLOW = FLOW;

  reach_check(FROM_S, S);
  reach_check(FROM_T, T);

  int num_edge = 0, diff;

  for (int i = 0; i < N; i++) {
    for (int k = 0; k < G[i].size(); k++) {
      if (G[i][k].is_rev == true || G[i][k].capacity == 0 ||
          can_reach[FROM_S][G[i][k].to] == false ||
          can_reach[FROM_T][i] == false)
        continue; // 逆辺または、最大流達成のために使用した辺はSKIP

      // ★★経路に重なりがあるかも知れないので、仮にcan_reachが両方trueでも、流量が1増えるとは限らない★★

      // 最大流達成の際に使用していない辺のみ、向きを逆転させれば、総経路が1増える可能性がある

      // 辺の向きを逆転させる
      G[i][k].capacity = 0;
      G[G[i][k].to][G[i][k].rev_index].capacity = 1;

      // S→T方向に流す
      diff = max_flow(S, T);

      if (diff == 1) { // 流量が増加した場合
        num_edge++;
        MAX_FLOW = FLOW + 1;

        if (i != T) {
          max_flow(T, i); // T→iに押し戻す
        }
        if (G[i][k].to != S) {
          max_flow(G[i][k].to, S); // G[i][k].to→Sに押し戻す
        }
      }

      // 辺の向きを元に戻す
      G[i][k].capacity = 1;
      G[G[i][k].to][G[i][k].rev_index].capacity = 0;
    }
  }

  printf("%d %d\\n", MAX_FLOW, num_edge);
}

int main() {

  while (true) {
    scanf("%d %d %d %d", &N, &M, &S, &T);
    if (N == 0 && M == 0 && S == 0 && T == 0)
      break;

    func();
  }

  return 0;
}