【网络流】hdu3376 Matrix Again

题意:给你一个N*N的矩阵,要求从起点(1,1)(往右或下方向)到终点(N,N)再从终点(往左或上)到起点,使一路上走过的点的权值和最大(走过的点可以再走,但是每个点的权值最多只能取一次)。
难度:2
题解:最大费用流。将矩阵上每个点拆成两个点Xi和Yi,从Xi向Yi连一条容量为1,费用为该点权值的负值的边,再连一条容量为1,费用为零的边。对于每个点i, 以及他有侧或下侧的点j(如果有的话)从Yi向Xj连一条容量为2,费用为零的边。新增一个附加源s和一个附加汇t,s向X(1,1)连一条容量为2,费用为零的边,Y(N,N)向t连一条容量为2,费用为零的边。求得的最小费用的负值就是对大费用。
#include<iostream>
using namespace std;
const int maxm=(600*600*2+2)*4;
const int maxn=600*600*2+2;
const int INF=100000000;
int map[600][600];
inline int min(int x,int y){return x<y?x:y;}
typedef struct node
{
    int v,flow,cost;
    node *next;
    node *rev();    
    }node;
node edge[maxm],*adj[maxn],*rec[maxn];
bool use[maxn];
int d[maxn],pre[maxn],q[10*maxm],len;
node inline *node::rev(){return edge+((this-edge)^1);}
inline void addedge(int u,int v,int flow,int cost)
{
    edge[len].v=v;edge[len].flow=flow;edge[len].cost=cost;
    edge[len].next=adj[u];adj[u]=&edge[len++];   
}
inline void insert(int u,int v,int flow,int cost)
{
    addedge(u,v,flow,cost);
    addedge(v,u,0,-cost);   
}    
bool SPFA(int n,int s,int t)
{ 
    int u,v,i,head=0,tail=0;q[tail++]=s;
    node *p;
    for(i=0;i<n;i++){d[i]=INF;use[i]=0;pre[i]=-1;}
    d[s]=0;use[s]=1; 
    while(head<tail)
    {      
          for(u=q[head++],use[u]=0,p=adj[u];p;p=p->next)
          {
                if(p->flow>0&&d[u]+p->cost<d[p->v])
                 {
                     d[p->v]=d[u]+p->cost;
                     pre[p->v]=u;rec[p->v]=p;
                     if(!use[p->v]){q[tail++]=p->v;use[p->v]=1;}                                
                }                                 
        }             
    }
    return d[t]==INF;      
}         
int mincostflow(int n,int s,int t)
{
    int u,v,mincost=0,tmp;
    while(!SPFA(n,s,t))
    {
          for(tmp=INF,v=t;v!=s;v=pre[v])tmp=min(tmp,rec[v]->flow);
          for(mincost+=tmp*d[t],v=t;v!=s;v=pre[v]){rec[v]->flow-=tmp;rec[v]->rev()->flow+=tmp;}             
    }
    return mincost;      
}      
int main()
{
    int n,m,i,j,k,s,t;
    while(scanf("%d",&n)!=EOF)
    {
          m=2;                    
          for(i=0;i<n*n*2+2;i++)adj[i]=NULL;
          len=0;s=n*n*2;t=s+1;
          
          for(i=0;i<n;i++)for(j=0;j<n;j++)scanf("%d",&map[i][j]);
          
          for(i=0;i<n;i++)
          for(j=0;j<n;j++)
          {
              k=i*n+j;
              insert(k*2,k*2+1,1,-map[i][j]);
              insert(k*2,k*2+1,m-1,0);            
          }                       
          
          for(i=0;i<n;i++)
          for(j=0;j<n-1;j++)
          {
              k=i*n+j;
              insert(2*k+1,2*(k+1),m,0);             
          }      
          
          for(i=0;i<n-1;i++)
          for(j=0;j<n;j++)
          {
              k=i*n+j;
              insert(2*k+1,2*(k+n),m,0);            
          }
          insert(s,0,2,0);
          insert(n*n*2-1,t,2,0);
          printf("%d\n",-mincostflow(t+1,s,t));        
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值