HDU2686Matrix && HDU3376 Matrix Again (最大费用最大流 && 经典建图)

来源:互联网 发布:rime输入法 mac 编辑:程序博客网 时间:2024/06/04 01:14

传送门:HDU 2686 Matrix

传送门:HDU 3376 Matrix Again

描述(HDU3376):

Matrix Again

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)
Total Submission(s): 4093    Accepted Submission(s): 1177


Problem Description
Starvae very like play a number game in the n*n Matrix. A positive integer number is put in each area of the Matrix.
Every time starvae should to do is that choose a detour which from the top left point to the bottom right point and than back to the top left point with the maximal values of sum integers that area of Matrix starvae choose. But from the top to the bottom can only choose right and down, from the bottom to the top can only choose left and up. And starvae can not pass the same area of the Matrix except the start and end..
Do you know why call this problem as “Matrix Again”? AS it is like the problem 2686 of HDU.
 

Input
The input contains multiple test cases.
Each case first line given the integer n (2<=n<=600) 
Then n lines, each line include n positive integers. (<100)
 

Output
For each test case output the maximal values starvae can get.
 

Sample Input
210 35 10310 3 32 5 36 7 1051 2 3 4 52 3 4 5 63 4 5 6 74 5 6 7 85 6 7 8 9
 

Sample Output
284680
 

PS: 这两道题目除了数据大小不同外是一模一样的

题意:

是给定一个n*n的矩阵,yifenfei从起点(1, 1)这个位置一直取数到(n,n),每取完一个数,下一个只能取当前数右方或者下方的一个数,(注意两个数之间的距离应该是1,之前以为下方或者右方任何一个数都可以取),就这样取到(n,n),然后再从(n,n)取回(1,1),这次每取完一个数,下一个只能取当前数左方或者右方的一个数,最后回到(1,1),每个数只能被取一次,求这样进行取数之后能取到的最大数

思路:

题目中问从左上角走到右下角,再从右下角回到左上角所能得到的最大权值,我们可以转化为从【左上角起点】到【右下角终点】走两次所获的最大权值。题目要求起点终点可以走多次,其他定只能走一次。按这种思路的话起点和终点的权值我们多算了一次,最后结果要减去。

把图看成 n * n个点,相互可到达的点之间建边, 可走次数作为边的容量,权值当做边的费用。问题就变成了最大费用最大流,求只需要取相反数结果即可。

建图过程:

把每个每个点 i 拆成左点 i 和右点 i + n * n。虚拟一个超级源点 outset = 0, 超级汇点 inset = 2 * n * n + 1。

(1)当i 为起点或终点时,左点向右点建边,边的容量为2, 因为起点终点可以走两次, 边的费用为点的权值。

(若起点和终点拆成容量为1的边,那我们在起点的时候就只有一个选择:向上走或者向下走,在终点时只能选择是由上面的点传到终点还是由左边的点传入终点,这样的话就相当于求走一次的最大权值和,显然这样建图是不对的。)

(2)当i 不为起点或终点时,左点向右点建边,边的容量为1, 因为只能走一次,边的费用为点的权值。

(3)相互可到达的点之间建边,如 u --> v,建边时 右点u 和左点 v 建边,边的容量最小为1,边的费用为0。这点要想明白!!!

(4)源点向起点的左点建边,边的容量为2,边的费用为0,终点的右点向汇点建边,边的容量为2,费用为0。


小结:

这一类的题给你一个N*M的矩阵,对应N*M个点,且每个点都有一定的点权。当第一次到达某个位置时,我们可以获得该位置的点权且只能获取这一次。

这里可以分为两种情况:

(1)要求是每个点只能走一次(除了起点和终点可以走多次),问你从左上角到右下角走两次所获取的最大权值和。

(2)要求是每个点可以走多次,问你从左上角到右下角走K次所获取的最大权值和。

情况(1)应该都可以用这种方式建图。

情况(2)有POJ 3422题,题解见Here


建图步骤有什么不理解的地方,可以看这篇博客:小比的博客。里面还讲了情况(2)。

 代码(HDU3376的):

#include <iostream>#include<cstdio>#include<queue>#include<cstring>using namespace std;//最小费用大流,求只需要取相反数结果即可。//点的总数为 N,点的编号 0~N -1const int maxn=610*610*2+2;const int maxm=4*maxn;const int inf=0x3f3f3f3f;struct Edge{  int to,next,cap,flow,cost;}es[maxm];int head[maxn],tol;int p[maxn];//上一条弧int d[maxn];//单位总费用bool vis[maxn];int n;int N;//节点总个数int outset,inset;int map[610][610];void init(int n){  N=n;  tol=0;  memset(head, -1, sizeof(head));}void addedge(int u,int v,int cap,int cost){  es[tol].to=v;  es[tol].cap=cap;  es[tol].cost=cost;  es[tol].flow=0;  es[tol].next=head[u];  head[u]=tol++;  es[tol].to=u;  es[tol].cap=0;  es[tol].cost=-cost;  es[tol].flow=0;  es[tol].next=head[v];  head[v]=tol++;}bool spfa(int s,int t){  queue<int>q;  for(int i=0; i<N; i++){    d[i]=inf;    vis[i]=false;    p[i]=-1;  }  d[s]=0;  vis[s]=true;  q.push(s);  while(!q.empty()){    int u=q.front();    q.pop();    vis[u]=false;    for(int i=head[u]; i!=-1; i=es[i].next){ //逆向枚举以u为起点的边      int v=es[i].to;      if(es[i].cap>es[i].flow && d[v]>d[u]+es[i].cost){        d[v]=d[u]+es[i].cost;        p[v]=i;        if(!vis[v]){ vis[v]=true; q.push(v);}      }    }  }  if(p[t]==-1)return false;  else return true;}//返回的是最大流,cost存的是最小费用int MCMF(int s,int t,int &cost){  int flow = 0;cost = 0;  while(spfa(s,t)){//每次寻找花销最小的路径    int Min=inf;    for(int i=p[t]; i!=-1; i=p[es[i^1].to]){      if(Min>es[i].cap-es[i].flow)      Min=es[i].cap-es[i].flow;    }    //增广    for(int i=p[t]; i!=-1; i=p[es[i^1].to]){      es[i].flow+=Min;      es[i^1].flow-=Min;      cost+=es[i].cost*Min;    }    flow+=Min;  }  return flow;}int change(int x,int y){  return (x-1)*n+y;}void getmap(){  int t=n*n;  outset=0;inset=2*n*n+1;  for(int i=1; i<=n; i++){    for(int j=1; j<=n; j++){      scanf("%d",&map[i][j]);      if(i==1&&j==1 || i==n&&j==n)        addedge(change(i,j), change(i,j)+t, 2, -map[i][j]);      else        addedge(change(i,j), change(i,j)+t, 1, -map[i][j]);      if(i<n)        addedge(change(i,j)+t, change(i+1,j), 1, 0);      if(j<n)        addedge(change(i,j)+t, change(i,j+1), 1, 0);    }  }  addedge(outset, 1, 2, 0);  addedge(change(n,n)+t, inset, 2, 0);}int main(){  #ifndef ONLINE_JUDGE  freopen("in.txt","r",stdin);  #endif    while(~scanf("%d",&n)){    init(2*n*n+2);    getmap();    int cost;    MCMF(outset, inset, cost);cost=-cost;    cost=cost-map[1][1]-map[n][n];    printf("%d\n",cost);  }  return 0;}





0 0
原创粉丝点击