航空路线问题(dp解法)

  题目链接:https://www.luogu.org/problemnew/show/P2770

 

 题意:

  从左到右给你n个点,有m条边连接这些点,问从最左边的点到达最右边的点再回到最左边的点最多可以经过几个点(除了起点外每个点最多只能被经过一次)。

  

 题解:

  首先,我们可以把题意转化成从最左边的点走两条不相交的路线到达最右边的点,且使经过的点最多。标程是最大费用最大流。

  为了限流,我们把每个点i拆成两个点xi,yi,x1->y1、xn->yn连一条容量为2,费用为1的边,其他点xi->yi连一条容量为1,费用为1的边。若有i->j可达(i<j),则连yi->xj最后求x1->yn的最大费用最大流即可。若最大流等于2,则有解,为最大费用-2(因为起点和终点重复计算了);否则无解。

 

  然而,其实这题还有另一种解法!!!

  我们设dpi,j表示两条路线分别走到了i点和j点。且只往>max(i,j)的点转移。

  有同学可能会问:那如果i<j-1,而且(i+1,j)这个状态要由(i,j)转移过来怎么办呢?

  其实完全不用担心这个问题,因为(i+1,j)这个状态可以由(i+1,j-x)的状态转移过来。

  我们可以这么想,假设我们已经知道了最终答案要经过哪些点,甲和乙现在同时站在起点,对于下一个要经过的点,若在甲的路线上,就让甲走到该点,在乙的路线上就让乙走到该点。所以只往最右边的点的右边走也是可以走出最优解的。

 

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<map>
 6 #define LL long long
 7 #define RI register int
 8 using namespace std;
 9 const int INF = 0x7ffffff ;
10 const int N = 100 + 10 ;
11 
12 inline int read() {
13     int k = 0 , f = 1 ; char c = getchar() ;
14     for( ; !isdigit(c) ; c = getchar())
15       if(c == '-') f = -1 ;
16     for( ; isdigit(c) ; c = getchar())
17       k = k*10 + c-'0' ;
18     return k*f ;
19 }
20 int n, m ; bool road[N][N] ; int dp[N][N], pre[N][N], typ[N][N], hh[N], gg[N] ; 
21 map<int,string>p ;
22 map<string,int>pp ;
23 
24 int dfs(int x,int y) {
25     if(dp[x][y]) return dp[x][y] ;
26     if(x+y == 1) return 0 ; 
27     int mm = min(x,y) ;
28     for(int i=0;i<mm;i++) {
29         if(road[i][x]) {
30             dp[y][x] = dp[x][y] = max(dp[x][y],dfs(i,y)+1) ;
31         }        
32         if(road[i][y]) {
33             dp[y][x] = dp[x][y] = max(dp[x][y],dfs(i,x)+1) ;
34         }    
35     }
36     if(!dp[x][y]) return -INF ;
37     return dp[x][y] ;
38 }
39 int tot = 0 ;
40 void dfss(int x,int y) {
41     hh[++tot] = x, gg[tot] = y ;
42     if(x+y == 1) return ;
43     int mm = min(x,y) ;
44     for(int i=0;i<mm;i++) {
45         if(dp[i][y] == dp[x][y]-1 && road[i][x]) {
46             dfss(i,y) ; return ;
47         }
48         if(dp[x][i] == dp[x][y]-1 && road[i][y]) {
49             dfss(x,i) ; return ;
50         }
51     }
52 }
53 
54 
55 int main() {
56     n = read(), m = read() ; string s ;
57     for(int i=1;i<=n;i++) {
58         cin>>s ; p[i] = s ; pp[s] = i ;
59     }
60     for(int i=1;i<=m;i++) {
61         string s1, s2 ; cin>>s1>>s2 ;
62         int x = pp[s1], y = pp[s2] ;
63         road[x][y] = road[y][x] = 1 ;
64         if(x == 1) road[0][y] = road[y][0] = 1 ;
65         else if(y == 1) road[0][x] = road[x][0] = 1 ;
66     }
67     dfs(n,n) ;
68     if(!dp[n][n]) { printf("No Solution!") ; return 0 ; }
69     printf("%d\n",dp[n][n]) ;
70     for(int i=1;i<n;i++) if(dp[i][n] == dp[n][n]-1) { dfss(i,n) ; break ; }
71     sort(hh+1,hh+tot+1) ; sort(gg+1,gg+tot+1) ;
72     int sz1 = unique(hh+1,hh+tot+1) - (hh+1) ;
73     int sz2 = unique(gg+1,gg+tot+1) - (gg+1) ;
74     cout<<p[1]<<"\n" ;
75     for(int i=1;i<=sz1;i++) if(hh[i] > 1) cout<<p[hh[i]]<<"\n" ;
76     for(int i=sz2;i;i--) if(gg[i] > 1) cout<<p[gg[i]]<<"\n" ; cout<<p[1]<<"\n" ;
77     return 0 ;
78 }

 

 

转载于:https://www.cnblogs.com/zub23333/p/8685992.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值