USACO 5.4 Canada Tour (DP / 最大费用最大流)
2015-04-01 20:25:02
思路:好题!
题意可以转化为:两个人从起点到终点(两人的路径不能有交点),仅能从左向右走,求最多能经过多少点。
方法1:DP:
想了很久... 基本可以想到用dp[i][j]来表示一人走到 i ,另一人走到 j 能经过的最多点数。但是转移方程怎么想都难以避免重复...
看了别人的博客,发现一个很巧妙的方法。
首先发现,dp[i][j] = dp[j][i](两个人可以换位置),dp[i][j] = max{dp[i][k]} (1<=k<j , k与j有边相连)
关于如何避免重复,我思考良久... 结果还是不太清晰。在转移方程时保证:j > i ,这样如果dp[i][k]的路径没有重复点,dp[i][j]的路径也不可能有重复点。
(这种转移方法每次只走一个人,而且走的是走完之后更右边的那个人。j > i)
方法2:网络流:
并没有想到这种做法... 百度了才知道还能用网络流做。
将每个点拆点(i 拆成 ia , ib),ia -> ib 建立容量为1,费用为1的边。为了实现两个走的条件,给起点1a->1b的边容量设为2,终点na->nb的边容量设为2。
然后根据题目的输入给点之间建立边,要注意建边条件:从左到右(当时这里坑了...)
最后跑一遍最大费用最大流,如果流量<2,那么答案就为1。否则输出费用即可。
DP:
1 /* 2 ID:naturec1 3 PROG: tour 4 LANG: C++ 5 */ 6 #include <cstdio> 7 #include <cstring> 8 #include <cstdlib> 9 #include <cmath> 10 #include <vector> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <string> 16 #include <iostream> 17 #include <algorithm> 18 using namespace std; 19 20 #define MEM(a,b) memset(a,b,sizeof(a)) 21 #define REP(i,n) for(int i=0;i<(n);++i) 22 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 23 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 24 #define MP(a,b) make_pair(a,b) 25 26 typedef long long ll; 27 typedef pair<int,int> pii; 28 const int INF = (1 << 30) - 1; 29 const int MAXN = 110; 30 31 int N,M,ans; 32 bool g[MAXN][MAXN]; 33 int dp[MAXN][MAXN]; 34 map<string,int> mp; 35 36 int main(){ 37 freopen("tour.in","r",stdin); 38 freopen("tour.out","w",stdout); 39 string str1,str2; 40 scanf("%d%d",&N,&M); 41 REP(i,N){ 42 cin >> str1; 43 mp[str1] = i + 1; 44 } 45 REP(i,M){ 46 cin >> str1 >> str2; 47 int id1 = mp[str1]; 48 int id2 = mp[str2]; 49 g[id1][id2] = g[id2][id1] = true; 50 } 51 dp[1][1] = 1; 52 for(int i = 1; i <= N; ++i){ 53 for(int j = i + 1; j <= N; ++j){ 54 dp[i][j] = -INF; 55 for(int k = 1; k < j; ++k) if(g[k][j] && dp[i][k] > 0){ 56 dp[i][j] = max(dp[i][j],dp[i][k] + 1); 57 } 58 dp[j][i] = dp[i][j]; 59 } 60 } 61 int ans = 1; 62 for(int i = 1; i <= N; ++i) if(g[i][N]){ 63 ans = max(ans,dp[i][N]); 64 } 65 printf("%d\n",ans); 66 return 0; 67 }
最大费用最大流:
1 /* 2 ID:naturec1 3 PROG: tour 4 LANG: C++ 5 */ 6 #include <cstdio> 7 #include <cstring> 8 #include <cstdlib> 9 #include <cmath> 10 #include <vector> 11 #include <map> 12 #include <set> 13 #include <stack> 14 #include <queue> 15 #include <string> 16 #include <iostream> 17 #include <algorithm> 18 using namespace std; 19 20 #define MEM(a,b) memset(a,b,sizeof(a)) 21 #define REP(i,n) for(int i=0;i<(n);++i) 22 #define FOR(i,a,b) for(int i=(a);i<=(b);++i) 23 #define getmid(l,r) ((l) + ((r) - (l)) / 2) 24 #define MP(a,b) make_pair(a,b) 25 26 typedef long long ll; 27 typedef pair<int,int> pii; 28 const int INF = (1 << 30) - 1; 29 const int MAXN = 210; 30 31 int N,M,st,ed; 32 map<string,int> mp; 33 int first[MAXN],ecnt; 34 int dis[MAXN],prev[MAXN],pree[MAXN]; 35 int inq[MAXN]; 36 37 struct edge{ 38 int v,next,cp,cost; 39 }e[MAXN * MAXN * 8]; 40 41 void Init(){ 42 MEM(first,-1); 43 ecnt = 0; 44 } 45 46 void Add_edge(int u,int v,int cp,int cost){ 47 e[ecnt].next = first[u]; 48 e[ecnt].v = v; 49 e[ecnt].cp = cp; 50 e[ecnt].cost = cost; 51 first[u] = ecnt++; 52 53 e[ecnt].next = first[v]; 54 e[ecnt].v = u; 55 e[ecnt].cp = 0; 56 e[ecnt].cost = -cost; 57 first[v] = ecnt++; 58 } 59 60 bool Spfa(){ 61 fill(dis + st,dis + ed + 1,-INF); 62 dis[st] = 0; 63 MEM(prev,-1); 64 MEM(inq,0); 65 queue<int> Q; 66 while(!Q.empty()) Q.pop(); 67 Q.push(st); 68 while(!Q.empty()){ 69 int x = Q.front(); Q.pop(); 70 inq[x] = 0; 71 for(int i = first[x]; ~i; i = e[i].next){ 72 if(e[i].cp <= 0) continue; 73 int v = e[i].v; 74 if(dis[v] < dis[x] + e[i].cost){ 75 dis[v] = dis[x] + e[i].cost; 76 prev[v] = x; 77 pree[v] = i; 78 if(inq[v] == 0){ 79 inq[v] = 1; 80 Q.push(v); 81 } 82 } 83 } 84 } 85 return prev[ed] != -1; 86 } 87 88 int MCMF(){ 89 int max_cost = 0,cnt = 0; 90 while(Spfa()){ 91 for(int i = ed; i != st; i = prev[i]){ 92 int id = pree[i]; 93 e[id].cp--; 94 e[id ^ 1].cp++; 95 } 96 max_cost += dis[N]; 97 cnt++; 98 } 99 if(cnt < 2) return -1; 100 return max_cost; 101 } 102 103 int main(){ 104 freopen("tour.in","r",stdin); 105 freopen("tour.out","w",stdout); 106 Init(); 107 string s1,s2; 108 scanf("%d%d",&N,&M); 109 REP(i,N){ 110 cin >> s1; 111 mp[s1] = i + 1; 112 } 113 st = 1; 114 ed = N + N; 115 Add_edge(1,1 + N,2,1); 116 FOR(i,2,N - 1) Add_edge(i,i + N,1,1); 117 Add_edge(N,N + N,2,1); 118 REP(i,M){ 119 cin >> s1 >> s2; 120 int id1 = mp[s1]; 121 int id2 = mp[s2]; 122 if(id1 < id2) Add_edge(id1 + N,id2,1,0); 123 else Add_edge(id2 + N,id1,1,0); 124 } 125 int ans = MCMF(); 126 if(ans < 0) ans = 1; 127 printf("%d\n",ans); 128 return 0; 129 }