題目:http://poj.org/problem?id=1422
題意:
n*n的方陣,存在若干條有向邊相連,選擇若干個點組成的路爲一條參觀路徑,所有的路徑都不存在相同點,選擇最少路徑,使得這些路徑包括所有節點,即最小路徑覆蓋問題
分析:
在有向無環圖中,假設沒有邊相連,則所有節點都是一條長度爲0的路勁,此時最小路徑覆蓋數爲總結點數
若此時多出一條邊1-2,則兩個節點1、2爲同一條路勁,此時最小路徑覆蓋數-1
若再多一條1-3,則最小路徑覆蓋數不變,因爲起點與上一次相同,即兩條邊不是匹配邊,只能選1-2或1-3
若多的第二條爲2-3,則與第一條路1-2爲同一條路勁,此時最小路徑覆蓋數-1
......
綜上,
最小路勁覆蓋數 == 總節點數 - 最大匹配數
代碼:
#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>
using namespace std;
#define MAX 200
#define MIN -1e5
#define INF 0x7f7f7f7f
int t, n, m;
int x[MAX], y[MAX]; // 行、列點集合
int Edge[MAX][MAX]; // 有向無環圖
int vis[MAX]; // path遞歸防止死循環的訪問數組
int path(int u) // 設置 或 更新最大匹配的邊
{
for(int v = 1; v<=n; v++) // 便利所有y(列)集合 節點
{
if(vis[v] == 0 && Edge[u][v] == 1) // 遞歸中未訪問 && 該列節點v與行節點u有變相連(有方向 u->v)
{
vis[v] = 1;
if(y[v] == -1 || path(y[v])) // 該列節點v沒有被行節點u匹配過 || 已經被行節點y【v】匹配過,嘗試是否能夠更新該匹配
{
x[u] = v;
y[v] = u;
return 1;
}
}
}
return 0;
}
void solve()
{
int ans = 0;
memset(x, -1, sizeof(x));
memset(y, -1, sizeof(y));
for(int u = 1; u<=n; u++) // 遍歷所有x行節點
{
if(x[u] == -1) // 未被匹配過
{
memset(vis, 0, sizeof(vis));
ans += path(u); // 匹配成功, 最大匹配數+1
}
}
printf("%d\n", n - ans); // 最小路徑覆蓋 == 總節點數 - 最大匹配數
}
int main()
{
int i, j;
//freopen("a.txt", "r", stdin);
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
int u, v;
memset(Edge, 0, sizeof(Edge));
for(i = 0; i<m; i++)
{
scanf("%d%d", &u, &v);
Edge[u][v] = 1;
}
solve();
}
return 0;
}