zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    1063. Set Similarity
    A1047. Student List for Course
    A1039. Course List for Student
    最大公约数、素数、分数运算、超长整数计算总结
    A1024. Palindromic Number
    A1023. Have Fun with Numbers
    A1059. Prime Factors
    A1096. Consecutive Factors
    A1078. Hashing
    A1015. Reversible Primes
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/8735587.html
Copyright © 2011-2022 走看看