程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> HDU 4862 Jump (最小K路徑覆蓋)

HDU 4862 Jump (最小K路徑覆蓋)

編輯:C++入門知識

HDU 4862 Jump (最小K路徑覆蓋)


HDU 4862 Jump

 

題意:給定一個N*M的矩陣,矩陣裡面為0~9的數字。現在規定從一個點可以跳到它正下方和正右方的點,花費的費用為曼哈頓距離 - 1。如果在跳的過程中,兩個點的數字相同,那麼將得到該點的數字。規定可以從任意點開始跳,每個點只能經過1次。最多可以選擇K個點來作為起點進行跳躍。問能否經過所有的點,如果可以,那麼花費的費用是多少。

 

思路:

如果是最小路徑覆蓋,那麼很容易構造圖。但這裡還有個限制是要在K次走完所有的點。

最小K路徑覆蓋的模型,用費用流或者KM算法解決,構造二部圖,X部有N*M個節點,源點向X部每個節點連一條邊,流量1,費用0,Y部有N*M個節點,每個節點向匯點連一條邊,流量1,費用0,如果X部的節點x可以在一步之內到達Y部的節點y,那麼就連邊x->y,費用為從x格子到y格子的花費能量減去得到的能量,流量1,再在X部增加一個新的節點,表示可以從任意節點出發K次,源點向其連邊,費用0,流量K,這個點向Y部每個點連邊,費用0,流量1,最後用這個圖跑最小費用最大流,如果滿流就是存在解,反之不存在,最小費用的相反數就是可以獲得的最大能量。

 

代碼:

 

/*
ID: [email protected]
PROG:
LANG: C++
*/
#include
#include #include #include #include #include #include #include #include #include #include #include #include using namespace std; #define INF (1 << 30) #define LINF (1LL << 60) #define PI acos(-1.0) #define mem(a, b) memset(a, b, sizeof(a)) #define rep(i, a, n) for (int i = a; i < n; i++) #define per(i, a, n) for (int i = n - 1; i >= a; i--) #define eps 1e-6 #define debug puts(===============) #define pb push_back #define mkp make_pair #define all(x) (x).begin(),(x).end() #define fi first #define se second #define SZ(x) ((int)(x).size()) #define POSIN(x,y) (0 <= (x) && (x) < n && 0 <= (y) && (y) < m) typedef long long ll; typedef unsigned long long ULL; int N, M, K; char mp[15][15]; const int maxn = 555; const int maxm = 5000; struct node { int v, cap, nxt, cost; } e[maxm * 2]; int g[maxn], cnt, st, ed, n, m; int ans, flow; void add(int u, int v, int cap, int cost) { e[++cnt].v = v; e[cnt].cap = cap; e[cnt].cost = cost; e[cnt].nxt = g[u]; g[u] = cnt; e[++cnt].v = u; e[cnt].cap = 0; e[cnt].cost = -cost; e[cnt].nxt = g[v]; g[v] = cnt; } void init() { cnt = 1; ans = flow = 0; st = N * M * 2; ed = st + 1; memset(g, 0, sizeof(g)); // 加邊 n = N * M; int tmp = ed + 1; add(st, tmp, K, 0); //最小K路徑覆蓋需要另外構造一個點,從匯點連流量為K的邊 for (int i = 0; i < n; i++) { add(st, i, 1, 0); // add(i + n, ed, 1, 0); add(tmp, i + n, 1, 0); } int c; for (int i = 0; i < N; i++) { for (int j = 0; j < M; j++) { int now = i * M + j; for (int l = i + 1; l < N; l++) { c = l - i - 1; if (mp[i][j] == mp[l][j]) c -= (mp[i][j] -'0'); add(now, l * M + j + n, 1, c); } for (int l = j + 1; l < M; l++) { c = l - j - 1; if (mp[i][j] == mp[i][l]) c -= (mp[i][j] - '0'); add(now, i * M + l + n, 1, c); } } } n = tmp; } int dis[maxn], que[maxn], pre[maxn]; bool vis[maxn]; bool spfa() { int font = 0, rear = 1; for(int i = 0; i <= n; i ++) { dis[i] = INF; vis[i] = false; } dis[st] = 0; que[0] = st; vis[st] = true; while(rear != font) { int u = que[font++]; font %= n; vis[u] = false; for(int i = g[u]; i; i = e[i].nxt) { int v = e[i].v; if(e[i].cap && dis[v] > dis[u] + e[i].cost) { dis[v] = dis[u] + e[i].cost; pre[v] = i; if(!vis[v]) { vis[v] = true; que[rear++] = v; rear %= n; } } } } if(dis[ed] == INF) return false; return true; } void augment() { int u, p, mi = INF; for(u = ed; u != st; u = e[p ^ 1].v) { p = pre[u]; mi = min(mi, e[p].cap); } for(u = ed; u != st; u = e[p ^ 1].v) { p = pre[u]; e[p].cap -= mi; e[p ^ 1].cap += mi; ans += mi * e[p].cost; // cost記錄的為單位流量費用,必須得乘以流量。 } flow += mi; } int MCMF() { init(); while(spfa()) augment(); if (flow != N * M) return -1; return -ans; } int main () { int T, cas = 1; scanf(%d, &T); while(T--) { scanf(%d%d%d, &N, &M, &K); for (int i = 0; i < N; i++) scanf(%s, mp[i]); int dd = MCMF(); printf(Case %d : %d , cas++, dd); } return 0; }

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved