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 }