poj 3422(最小费用最大流)

现在正享受着彻底征服一道题的快感... 爽

做这题是曲折的, 首先拿到题目马上想的是用k次dp来做。 很快的敲好了,然后果断wa。 后面想不通于是自己生成数据, 发现确实dp很难解决这个问题,因为k次dp的做法在这题中已相当于一种贪心, 而这题明显贪心是有问题的.

看看这个数据就知道了:

4 3
1 2 3 5
0 2 1 1
1 4 2 3
3 4 1 2

发现如果每次找都找最大路的时候,最后的结果是32, 明显结果是34,这就是每步最优却不能保证最后的结果最优.

这时发现,网络流的回流可以很好的解决这一问题,因为有回流所以可以保证k次流时费用最小。  因为网络流算法在后续的寻找增广路的过程中,通过回流可以改变之前可行流的流向(我是多么羡慕网络流的这种性质,错了可以后续来弥补,从而使结果最好,可是人生却只能想一次dfs ,只能往下走,无法回头 ).  

至于为什么要用最小费用, 题中不是要求最大值吗?, 这个问题很好解决,你可以在找可行流的时候用最大路径算法,也可以将每个点的费用变成负值。

建图就比较简单了因为题中限制了每个点只能用一次,所以拆点就很必要了. 

                                    Kaka's Matrix Travels
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 6277   Accepted: 2483

Description

On an N × N chessboard with a non-negative number in each grid, Kaka starts his matrix travels with SUM = 0. For each travel, Kaka moves one rook from the left-upper grid to the right-bottom one, taking care that the rook moves only to the right or down. Kaka adds the number to SUM in each grid the rook visited, and replaces it with zero. It is not difficult to know the maximum SUM Kaka can obtain for his first travel. Now Kaka is wondering what is the maximum SUM he can obtain after his Kth travel. Note the SUM is accumulative during the K travels.

Input

The first line contains two integers N and K (1 ≤ N ≤ 50, 0 ≤ K ≤ 10) described above. The following N lines represents the matrix. You can assume the numbers in the matrix are no more than 1000.

Output

The maximum SUM Kaka can obtain after his Kth travel.

Sample Input

3 2
1 2 3
0 2 1
1 4 2

Sample Output

15

Source

POJ Monthly--2007.10.06, Huang, Jinsong
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <iostream>
using namespace std;
#define N 5050
#define INF 0x3fffffff

struct node
{
    int to,next,w,c;
}edge[N*N];

int n,k;
int s,t;
int cnt,pre[N];
int que[N*100];
int point[N],pedge[N];

void add_edge(int u,int v,int w,int c)
{
    edge[cnt].to=v;
    edge[cnt].w=w;
    edge[cnt].c=c;
    edge[cnt].next=pre[u];
    pre[u]=cnt++;
}

int spfa()
{
    int qf=1,qd=0;
    int dis[N];
    int mark[N];
    memset(point,-1,sizeof(point));
    memset(pedge,-1,sizeof(pedge));
    for(int i=0;i<=t;i++)
    {
        dis[i]=INF;
        mark[i]=0;
    }
    que[0]=s;
    mark[s]=1;
    dis[s]=0;
    while(qf>qd)
    {
        int cur=que[qd++];
        mark[cur]=0;
        for(int p=pre[cur];p!=-1;p=edge[p].next)
        {
            int v=edge[p].to;
            int w=edge[p].w;
            int c=edge[p].c;
            if(w==0) continue;
            if( dis[v]>dis[cur]+c )
            {
                dis[v]=dis[cur]+c;
                point[v]=cur;
                pedge[v]=p;
                if(mark[v]==0)
                {
                    mark[v]=1;
                    que[qf++]=v;
                }
            }
        }
    }
    if(dis[t]==INF) return 0;
    else return 1;
}

int fuc()
{
    int sum=0;
    int tmp=t;
    while( tmp != 0 )
    {
        int p = pedge[tmp];
        sum += edge[p].c;
        edge[p].w -= 1;
        edge[p^1].w += 1;
        tmp = point[tmp];
    }
    return sum;
}

int main()
{
    while(scanf("%d%d",&n,&k)!=EOF)
    {
        cnt=0;
        memset(pre,-1,sizeof(pre));

        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
            {
                int id=(i-1)*n+j;
                int tmp;
                scanf("%d",&tmp);
                add_edge(id,n*n+id,1,-tmp);
                add_edge(n*n+id,id,0,tmp);
                        
                add_edge(id,n*n+id,k-1,0);
                add_edge(n*n+id,id,0,0);

                if(i!=n)
                {
                    add_edge(n*n+id,id+n,k,0);
                    add_edge(id+n,n*n+id,0,0);
                }
                if(j!=n)
                {
                    add_edge(n*n+id,id+1,k,0);
                    add_edge(id+1,n*n+id,0,0);
                }
            }
        s=0;
        t=n*n*2+1;
        add_edge(s,1,k,0);
        add_edge(1,s,0,0);
        
        add_edge(n*n*2,t,k,0);
        add_edge(t,2*n*n,0,0);
        ///////////  建图完成
        int sum=0;
        while( spfa() )
        {
            sum+=fuc();
        }
        printf("%d\n",-sum);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/chenhuan001/p/2950793.html