poj 3422 费用流

题目描述:

题目比较短。选择一个矩阵里,从左上角到右下角的,前k条路径的最大和。


解题思路:

   和网络流思想类似。因为贪心选择第一条最大之后,继续选择的第二条最大,并不意味着两条和最大。因而需要最大流的反向更新的思想。

   构图上,因为网络流的流量,费用是在边上。这里是点。所以需要做拆点构图。


代码:

#include <stdio.h>
#include <queue>
#define N 2510
#define MIN -10000000

using namespace std;

typedef struct EDGE{
    int id;
    int flow, fee;
    struct EDGE *next;
}EDGE;

typedef struct NODE{
    int edge_num;
    struct EDGE *next;
}NODE;

int tr[51][51], n, k, start, end, sum=0, pre[2*N];
NODE dag[2*N];

void spfa(){ // from start to end
    int fee[2*N], i, visit[2*N], v;
    for(i=0; i<2*N; i++)
        fee[i] = MIN;
    fee[start] = 0;
    //memset(visit,0,sizeof(visit));
    for(i=0;i<2*N;i++)
        visit[i] = 0;
    
    queue<int> q;
    q.push(start);
    while(!q.empty()){  
        v = q.front();
        q.pop();
        visit[v] = 0;//pop后标记为未访问 
        EDGE* pEdge = dag[v].next;
        while(pEdge!=NULL) {
            if( (pEdge->flow>0) && (fee[pEdge->id] < fee[v]+pEdge->fee)){
                fee[pEdge->id] = fee[v]+pEdge->fee;
                pre[pEdge->id] = v;
                if(!visit[pEdge->id]){
                    visit[pEdge->id] = 1;
                    q.push(pEdge->id);
                }   
            }
            pEdge = pEdge -> next;
        }
    }
}

main(){
    int i, j;
    
    scanf("%d %d",&n, &k);
    
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            scanf("%d",&tr[i][j]);
        }
    }
    int ver_num = n*n;
    start = 0;
    end = 2*ver_num-1;
    //构图:change tr[][] to dag[]
    EDGE edge[6*N];
    int edge_index=0;
    for(i=0;i<n;i++){
        for(j=0;j<n;j++){
            int index_in = i*n + j;
            int index_out = index_in+ver_num;
            //in
            dag[index_in].edge_num = 0;
            
            //对每个in,添加向 out 的边 
            dag[index_in].edge_num ++; 
            edge[edge_index].id = index_out;
            edge[edge_index].flow = k;
            edge[edge_index].fee = tr[i][j];
            edge[edge_index].next = NULL;
            
            dag[index_in].next = &edge[edge_index];
            edge_index++;
            //对每个in,添加向 左的边
            if(j>0){
                dag[index_in].edge_num ++;
                edge[edge_index].id = index_out - 1;
                edge[edge_index].flow = 0;
                edge[edge_index].fee = 0;
                edge[edge_index].next = NULL;
                
                edge[edge_index-1].next = &edge[edge_index];
                edge_index++;
                
            }
            
            //对每个in,添加向 上的边
            if(i>0){
                dag[index_in].edge_num ++;
                edge[edge_index].id = index_out - n;
                edge[edge_index].flow = 0;
                edge[edge_index].fee = 0;
                edge[edge_index].next = NULL;
                
                edge[edge_index-1].next = &edge[edge_index];
                edge_index++;  
            } 
            
            //out
            dag[index_out].edge_num = 0;
            dag[index_out].next = NULL;
            
            //对每个out,添加向 in 的边
            edge[edge_index].id = index_in;
            edge[edge_index].flow = 0;
            edge[edge_index].fee = 0;
            edge[edge_index].next = NULL;
            
            dag[index_out].edge_num ++;
            dag[index_out].next = &edge[edge_index];
            edge_index ++;
            //对每个out,添加向 右的边 
            if(j<n-1){
                dag[index_out].edge_num ++;
                
                edge[edge_index].id = index_in + 1;
                edge[edge_index].flow = k;
                edge[edge_index].fee = 0;
                edge[edge_index].next = NULL;
                
                edge[edge_index-1].next = &edge[edge_index];
                
                edge_index ++;
            }
            //对于每个out,添加向 下的边 
            if(i<n-1){
                dag[index_out].edge_num ++;
                
                
                edge[edge_index].id = index_in + n;
                edge[edge_index].flow = k;
                edge[edge_index].fee = 0;
                edge[edge_index].next = NULL;
                
                edge[edge_index-1].next = &edge[edge_index];
   
                edge_index++;
            }            
        } 
    }
    //find k path
    int cur;
    while(k){
        spfa();
        //update flow and fee
        cur = end;
        i = pre[end];
        EDGE *pEdge = NULL;
        while(cur!=start){
            int tmp=0;
            
            pEdge = dag[i].next;
            while(pEdge->id!=cur)
                pEdge = pEdge -> next;
            (pEdge->flow) --;
            if(pEdge->fee != 0){
                tmp = pEdge->fee;
                sum += (pEdge->fee);
                pEdge->fee = 0;
            }
            
            pEdge = dag[cur].next;
            while(pEdge->id!=i)
                pEdge = pEdge -> next;
            (pEdge->flow) ++;
            pEdge->fee = 0-tmp;
            
            cur = i;
            i = pre[i];
        }
        k--;    
    } 
    printf("%d\n",sum);
    system("pause");
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值