UVa 11159 Factors and Multiples

問題概要

2 3 4 5
6 7 8 9
というような入力がくる。
上段×任意の整数 = 下段 になるような組み合わせを線で結ぶ。
数字を取り除いてその線をなくすためには、最低何個の数字を取り除かなければならないか。

解法

最小点カバー。
2部マッチングと同値になる。

ソース

#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<sstream>
#include<cassert>
#include<queue>
#include<stack>
#include<bitset>
#include<cstring>

#define REP(i,b,n) for(int i=b;i<(int)n;i++)
#define rep(i,n)   REP(i,0,n)
#define ALL(C)     (C).begin(),(C).end()
#define FOR(it,o)  for(__typeof((o).begin()) it=(o).begin(); it!=(o).end(); ++it)

using namespace std;
const double EPS = 0.00000001;
const int INF = 1000000000;
typedef long long lli;


const int MAX_V = 1000;
 
int V;
vector<int> G[MAX_V];
int match[MAX_V];
bool used[MAX_V];
 

void add_edge(int u, int v){
  G[u].push_back(v);
  G[v].push_back(u);
}
 

bool dfs(int v){
  used[v] = true;
  rep(i, (int)G[v].size()){
    int u = G[v][i], w = match[u];
    if(w < 0 || (!used[w] && dfs(w))){
      match[v] = u;
      match[u] = v;
      return true;
    }
  }
  return false;
}
 

int bipartite_matching(){
  int res = 0;
  memset(match, -1,  sizeof(match));
  rep(i, V){
    if(match[i] < 0){
      memset(used, 0, sizeof(used));
      if(dfs(i)){
        res++;
      }
    }
  }
  return res;
}


int main(){
  int T;
  cin >> T;
  rep(tc, T){
    rep(i, 1000){
      G[i].clear();
    }
    int t[2][1000];
    int n[2];
    rep(i, 2){
      cin >> n[i];
      rep(j, n[i]){
        cin >> t[i][j];
      }
    }
    rep(i, n[0]){
      rep(j, n[1]){
        if(t[1][j] == 0)add_edge(i, n[0] + j);
        else if(t[0][i] == 0)continue;
        else if(t[1][j] % t[0][i] == 0){
            add_edge(i, n[0] + j);
        }
      }
    }
    V = n[0] + n[1];
    printf("Case %d: %d\n", tc + 1, bipartite_matching());
  }
  return 0;
}