1. 程式人生 > 實用技巧 >HDU - 4370 0 or 1 思維,最短路

HDU - 4370 0 or 1 思維,最短路

給定n * n矩陣C ij(1 <= i,j <= n),我們要找到0或1的n * n矩陣X ij(1 <= i,j <= n)。
此外,X ij滿足以下條件:
1.X 12 + X 13 + ... X 1n = 1
2.X 1n + X 2n + ... X n-1n = 1
3.對於每個i(1 <i <n),滿足ΣXki(1 <= k <= n)=ΣXij(1 <= j <= n)。
例如,如果n = 4,我們可以得到以下等式:
X 12 + X 13 + X 14 = 1
X 14 + X 24 + X 34 = 1
X 12 + X 22 + X 32 + X 42 = X 21 + X 22 + X 23 + X 24
X 13 + X 23 + X 33 + X 43 = X 31 + X 32 + X 33 + X 34
現在,我們想知道你可以得到的最小ΣCij * X ij(1 <= i,j <= n)。

給定的性質可以想到以下等價變化:

將xij轉化為ij圖的鄰接矩陣。

1號點在2-n中有一個為1,其餘0,相當於出度為1,n號點同理,入度為1 。其餘點隨意。

和C對應相乘相當於在C上找一條路徑使得路徑和最小。這樣就轉化成了最短路。

考慮最短路滿足的條件 1.1-n的最短路 2.1到1的一個環,n到n的一個環,兩者的疊加。

對於後者,只需要for一遍所有節點用已知的最短路維護即可,

int n, m;//n點數 m邊數
struct edge { int v, w; edge(int a, int b) { v = a, w = b; } };//v終點,w邊權
struct node {
    
int id, dis;//id點編號 dis暫時距離 node(int a, int b) { id = a, dis = b; } friend bool operator<(node a, node b) { return a.dis > b.dis;//每次讓距離小出隊 } }; vector<edge>e[maxn]; int dis[maxn];//記錄最短路 bool done[maxn];//記錄是否找到最短路 int Min1, Min2; void dijkstra(int t) { int s = t;//s起點 根據情況改
for (int i = 0; i <= n; i++) dis[i] = INF, done[i] = 0; dis[s] = 0; priority_queue<node>Q; Q.push(node(s, 0)); while (!Q.empty()) { node u = Q.top(); Q.pop(); if (done[u.id])continue; done[u.id] = 1; for (int i = 0; i < e[u.id].size(); i++) {//遍歷鄰居 edge y = e[u.id][i]; if (done[y.v])continue; if (dis[y.v] > y.w + dis[u.id]) { dis[y.v] = y.w + u.dis; Q.push(node(y.v, dis[y.v]));//更新最短路 } } } if (t == 0) { for (int i = 0; i < n; i++) { if (i == t) continue; Min1 = min(Min1, dis[i] + e[i][t].w); } } else { for (int i = 0; i < n; i++) { if (i == t) continue; Min2 = min(Min2, dis[i] + e[i][t].w); } } } int main() { int res; int x; while (~scanf("%d", &n)) { Min1 = Min2 = INF; memset(e, 0, sizeof e); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) x = readint(), e[i].push_back(edge(j, x)); } for (int i = 0; i < n; i++) e[i][i].w = INF; dijkstra(0); res = dis[n - 1]; dijkstra(n - 1); printf("%d", min(res, (Min1 + Min2))); puts(""); } }