• 51Nod 1084 矩阵取数问题 V2 —— 最小费用最大流 or 多线程DP


    题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1084

    基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题
     收藏
     关注
    一个M*N矩阵中有不同的正整数,经过这个格子,就能获得相应价值的奖励,先从左上走到右下,再从右下走到左上。第1遍时只能向下和向右走,第2遍时只能向上和向左走。两次如果经过同一个格子,则该格子的奖励只计算一次,求能够获得的最大价值。
     
    例如:3 * 3的方格。
     
    1 3 3
    2 1 3
    2 2 1
     
    能够获得的最大价值为:17。1 -> 3 -> 3 -> 3 -> 1 -> 2 -> 2 -> 2 -> 1。其中起点和终点的奖励只计算1次。
     
    Input
    第1行:2个数M N,中间用空格分隔,为矩阵的大小。(2 <= M, N <= 200)
    第2 - N + 1行:每行M个数,中间用空格隔开,对应格子中奖励的价值。(1 <= A[i,j] <= 10000)
    Output
    输出能够获得的最大价值。
    Input示例
    3 3
    1 3 3
    2 1 3
    2 2 1
    Output示例
    17

    题解:

    1.实际上就是求两条从左上角到右下角的路径的权值和最大(重叠部分只算一次)。

    2.感觉上,最优情况下的两条路径是不会有重叠部分的。但这只是感觉,缺乏证明,但就算两条路径可以有重叠部分,问题也可以照样解决。

    3.一拿到题目首先就想到了DP,但是因为有两条路径,如果进行两次DP,在第一次DP后,把路径上的值全部清零,然后再进行一次DP,所得的结果不一定是最优的。因此:两条路径必须同时考虑,而不能割裂开来。

    4.但是当时真的想不出怎么个DP,于是自己就朝着暴力一点的方向去思考,于是想到了图论之网络流,结果还真行得通:

    如图:

    1) 把每个格子拆成两点,一个点作为入点,一个点作为出点,从入点向出点引两条边,第一条边的容量为1,单位花费为-a[i][j],第二条的容量为1,单位花费为0。

    2) 对于每个格子,从出点引一条边向右边格子的入点,容量为2,单位花费为0。

    3) 以第一个格子的入点为源点,以最后一个格子的出点为汇点,跑最小费用最大流,得到的最小花费的相反数,即为答案。

    正确性证明:

    1) 由于每相邻两个点直接的容量都为2,所以就满足了“两条路径”。

    2) 在一个格子里,入点和出点之间,只有一条边(一个容量)的花费是有效的,这就满足了“路径重叠部分的值只取一次”。

    3) 最小费用最大流:可知最大流为2,这就满足了“两条路径”,而最小费用,即为最大费用的相反数,所以满足了权值和最大。

    多线程DP做法:

    1.可以在DP的时候,把两条路径也考虑进去。

    2.设dp[step][x1][x2]:为走了step步,第一条路径的当前x坐标未x1,第二条的x坐标为x2,知道步数和x坐标,就可以知道y坐标了。详情请看代码。

    (此题用网络流居然跑得比DP还快)

    最小费用最大流:

      1 #include <iostream>
      2 #include <cstdio>
      3 #include <cstring>
      4 #include <algorithm>
      5 #include <vector>
      6 #include <cmath>
      7 #include <queue>
      8 #include <stack>
      9 #include <map>
     10 #include <string>
     11 #include <set>
     12 #define ms(a,b) memset((a),(b),sizeof((a)))
     13 using namespace std;
     14 typedef long long LL;
     15 const int INF = 2e9;
     16 const LL LNF = 9e18;
     17 const int mod = 1e9+7;
     18 const int MAXN = 1e5+10;
     19 
     20 struct Edge
     21 {
     22     int to, next, cap, flow, cost;
     23 }edge[1000000];
     24 int tot, head[MAXN];
     25 int pre[MAXN], dis[MAXN];
     26 bool vis[MAXN];
     27 int N;
     28 
     29 void init(int n)
     30 {
     31     N = n;
     32     tot = 0;
     33     memset(head, -1, sizeof(head));
     34 }
     35 
     36 void add(int u, int v, int cap, int cost)
     37 {
     38     edge[tot].to = v; edge[tot].cap = cap; edge[tot].cost = cost;
     39     edge[tot].flow = 0; edge[tot].next = head[u]; head[u] = tot++;
     40     edge[tot].to = u; edge[tot].cap = 0; edge[tot].cost = -cost;
     41     edge[tot].flow = 0; edge[tot].next = head[v]; head[v] = tot++;
     42 }
     43 
     44 bool spfa(int s, int t)
     45 {
     46     queue<int>q;
     47     for(int i = 0; i<=N; i++)
     48     {
     49         dis[i] = INF;
     50         vis[i] = false;
     51         pre[i] = -1;
     52     }
     53 
     54     dis[s] = 0;
     55     vis[s] = true;
     56     q.push(s);
     57     while(!q.empty())
     58     {
     59         int u  = q.front();
     60         q.pop();
     61         vis[u] = false;
     62         for(int i = head[u]; i!=-1; i = edge[i].next)
     63         {
     64             int v = edge[i].to;
     65             if(edge[i].cap>edge[i].flow && dis[v]>dis[u]+edge[i].cost)
     66             {
     67                 dis[v] = dis[u]+edge[i].cost;
     68                 pre[v] = i;
     69                 if(!vis[v])
     70                 {
     71                     vis[v] = true;
     72                     q.push(v);
     73                 }
     74             }
     75         }
     76     }
     77     if(pre[t]==-1) return false;
     78     return true;
     79 }
     80 
     81 int minCostMaxFlow(int s, int t, int &cost)
     82 {
     83     int flow = 0;
     84     cost = 0;
     85     while(spfa(s,t))
     86     {
     87         int Min = INF;
     88         for(int i = pre[t]; i!=-1; i = pre[edge[i^1].to])
     89         {
     90             if(Min>edge[i].cap-edge[i].flow)
     91                 Min = edge[i].cap-edge[i].flow;
     92         }
     93         for(int i = pre[t]; i!=-1; i = pre[edge[i^1].to])
     94         {
     95             edge[i].flow += Min;
     96             edge[i^1].flow -= Min;
     97             cost += edge[i].cost*Min;
     98         }
     99         flow += Min;
    100     }
    101     return flow;
    102 }
    103 
    104 int a[220][220];
    105 int main()
    106 {
    107     int n, m;
    108     scanf("%d%d",&m,&n);
    109     for(int i = 0; i<n; i++)
    110     for(int j = 0; j<m; j++)
    111         scanf("%d",&a[i][j]);
    112 
    113     int N = n*m;
    114     int st = 0, en = 2*N-1;
    115     init(2*N);
    116 
    117     for(int i = 0; i<n; i++)
    118     for(int j = 0; j<m; j++)
    119     {
    120         /*把格子拆成两个点,一个点进,一个点出,中间有两条边:
    121           一条边的容量为1,单位花费为-a[i][j],
    122           另一条的容量为1,单位花费为0.
    123           这样就使得任意一个格子,可以在两条路径上,但只能被计算一次
    124         */
    125         add(i*m+j,N+i*m+j,1,-a[i][j]);
    126         add(i*m+j,N+i*m+j,1,0);
    127 
    128         if(j!=m-1) add(N+i*m+j,i*m+j+1,2,0);    //引向右边的格子
    129         if(i!=n-1) add(N+i*m+j,(i+1)*m+j,2,0);  //引向下边的格子
    130     }
    131 
    132     int ans;
    133     minCostMaxFlow(st,en,ans);
    134     printf("%d
    ",-ans);    //取反
    135 }
    View Code

    多线程DP:

     1 #include <iostream>
     2 #include <cstdio>
     3 #include <cstring>
     4 #include <algorithm>
     5 #include <vector>
     6 #include <cmath>
     7 #include <queue>
     8 #include <stack>
     9 #include <map>
    10 #include <string>
    11 #include <set>
    12 using namespace std;
    13 typedef long long LL;
    14 const int INF = 2e9;
    15 const LL LNF = 9e18;
    16 const int MOD = 1e9+7;
    17 const int MAXN = 1e6+10;
    18 
    19 int a[220][220], dp[440][220][220];
    20 int main()
    21 {
    22     int n, m;
    23     while(scanf("%d%d",&m,&n)!=EOF)
    24     {
    25         for(int i = 0; i<n; i++)
    26         for(int j = 0; j<m; j++)
    27             scanf("%d",&a[i][j]);
    28 
    29         memset(dp, 0, sizeof(dp));
    30         dp[0][0][0] = a[0][0];
    31         for(int step = 1; step<=n+m-2; step++)
    32         for(int x1 = 0; x1<=min(n-1,step); x1++)
    33         for(int x2 = 0; x2<=min(n-1,step); x2++)
    34         {
    35             int y1 = step-x1;
    36             int y2 = step-x2;
    37             int flag = (x1!=x2)||(y1!=y2);  //判断两条路径的当前位置是否重叠
    38             int t1 = 0, t2 = 0, t3 = 0, t4 = 0;
    39             if(x1!=0&&x2!=0) t1 = dp[step-1][x1-1][x2-1];
    40             if(x1!=0&&y2!=0) t2 = dp[step-1][x1-1][x2];
    41             if(y1!=0&&x2!=0) t3 = dp[step-1][x1][x2-1];
    42             if(y1!=0&&y2!=0) t4 = dp[step-1][x1][x2];
    43             dp[step][x1][x2] = max(max(t1,t2),max(t3,t4))+a[x1][y1]+flag*a[x2][y2];
    44         }
    45 
    46         printf("%d
    ", dp[n+m-2][n-1][n-1]);
    47     }
    48 }
    View Code
  • 相关阅读:
    Python 双向链表的实现
    Python 单链表实现
    分享两种 Python 中的单例模式
    HTML 盒子模型
    HTML 行高
    HTML 超链接 文本修饰 背景属性
    HTML 标签分类
    HTML 文字,样式表
    HTML CSS
    HTML 锚点 空链 超链优化写法
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8735587.html
Copyright © 2020-2023  润新知