poj 3422(拆点费用流)

题意:有个方阵,每个格子里都有一个非负数,从左上角走到右下角,每次走一步,只能往右或往下走,经过的数字拿走 

每次都找可以拿到数字和最大的路径走,走k次,求最大和 


如下图拆点 ,

     




#include<cstdio>
#include<cstring>
#include<map>
#include<vector>
#include<cmath>
#include<cstdlib>
#include<stack>
#include<queue>
#include <iomanip>
#include<iostream>
#include<algorithm>
using namespace std ;
const int N= 6000 ;
const int M= N*5 ;
const int inf = 1<< 30 ;
struct node
{
	int u ,v,c,cost,next ;
}edge[M] ;
int head[N],pp[N],pre[N],vist[N],dist[N] ;
int top ;
int g[100][100];

void add(int u ,int v,int c,int cost)      
{      
    edge[top].u=u;      
    edge[top].v=v;      
    edge[top].c=c;      
    edge[top].cost=cost;      
    edge[top].next=head[u];      
    head[u]=top++;      
    edge[top].u=v;      
    edge[top].v=u;      
    edge[top].c=0;      
    edge[top].cost=-cost;      
    edge[top].next=head[v];      
    head[v]=top++;      
}      
      
int SPFA(int s,int t)      
{      
    int u , v ;      
    memset(vist,0,sizeof(vist));      
    memset(pre,-1,sizeof(pre));      
    for(int i = 0 ; i <= t ; i++)  dist[i]=inf ;      
    vist[s]=1;dist[s]=0;pre[s]=s;      
    queue<int>q;      
    q.push(s);      
    while(!q.empty())      
    {      
         u=q.front();      
         q.pop();      
         vist[u]=0;      
         for(int i =head[u];i!=-1;i=edge[i].next)      
         {      
               v=edge[i].v;      
               if(edge[i].c && dist[v] > dist[u]+edge[i].cost)      
               {      
                    dist[v] = dist[u]+edge[i].cost ;      
                     pre[v]=u;      
                     pp[v]=i;      
                     if(!vist[v]);      
                     {      
                           vist[v]=1;      
                           q.push(v);      
                     }      
               }      
        }      
    }      
    if(dist[t]==inf) return 0;      
    return 1 ;      
}      
      
int MFMC(int s,int t)      
{      
    int mincost=0,flow=0,minflow ;      
    while(SPFA(s,t))      
    {      
          minflow=inf;      
          for(int i=t;i!=s;i=pre[i])      
              minflow=min(minflow,edge[pp[i]].c);      
          for(int i=t;i!=s;i=pre[i])      
          {      
                edge[pp[i]].c -= minflow;      
                edge[pp[i]^1].c += minflow;      
          }          
          flow += minflow;      
          mincost += dist[t]*minflow ;      
        //  printf("****");  
    }      
    return -mincost ;      
}      

int main()
{
	  int n,k ;
	  while(~scanf("%d%d",&n,&k))
	  {
	  	        top = 0 ; 
				memset(head,-1,sizeof(head)) ;
				memset(g,0,sizeof(g)) ;
				int s = 0 ,t = n*n*2+1 ;
				for(int i = 1 ; i <= n ; i++)
				   for(int  j = 1 ; j <= n ; j++)
				      {
				      	  int tmp = (i-1)*n+j ;
				      	  scanf("%d",&g[i][j]) ;
				      	  add(tmp,tmp+n*n,1,-g[i][j]) ;
						  add(tmp,tmp+n*n,k-1,0) ;	 
				      }
			  for(int i = 1 ; i <= n ; i++)
			   for(int j = 1 ; j <= n ; j++)
			    {
			    	 int tmp = (i-1)*n+j ;
			    	 int a = (i-1)*n+j+1 ;
					 int b= i*n+j ; 
			    	 if(j<n)
			    	   add(tmp+n*n,a,k,0);
			    	 if(i<n)
					   add(tmp+n*n,b,k,0);  
			    }		
			  add(s,1,k,0);
			  add(n*n*2,t,k,0);
			  int ans = MFMC(s,t);
			  printf("%d\n",ans);	  
	  }
	return 0 ;
}
 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值