zoukankan      html  css  js  c++  java
  • 航空路线问题(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
    ",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]<<"
    " ;
    75     for(int i=1;i<=sz1;i++) if(hh[i] > 1) cout<<p[hh[i]]<<"
    " ;
    76     for(int i=sz2;i;i--) if(gg[i] > 1) cout<<p[gg[i]]<<"
    " ; cout<<p[1]<<"
    " ;
    77     return 0 ;
    78 }

     

  • 相关阅读:
    跟小静学CLR via C#(12)委托Delegate
    跟小静读CLR via C#(02)基元类型、引用类型、值类型
    跟小静读CLR via C#(07)静态类,分部类
    jQuery折叠菜单
    ajax调用后台Datatable
    跟小静读CLR via C#(11)无参属性、索引器
    跟小静读CLR via C#(08)操作符
    跟小静读CLR via C#(05) 访问限定、数据成员
    AjaxPro排错指南
    跟小静读CLR via C#(14)可空值类型,关于?和??的故事
  • 原文地址:https://www.cnblogs.com/zub23333/p/8685992.html
Copyright © 2011-2022 走看看