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/archive/2013/03/08/2950793.html

百度百科创建词条工具V2.7 我们先了解一下百度百科的优势: 1.权威性,广大网民对百度百科信任度特别高,容易受百科中相关信息影响 2.排名好,绝大部分百科词条(即关键词)能排至各大搜索引擎的前三位 3.流量大,一般词条每天浏览量不亚于一个中型企业站每天的总流量 4.转化率高,百科成为网民上网查资料必看的网站,转化为客户的几率较PPC及PM广告大得多。如病人及其家属查找某疾病如何治疗时,必看百科 5.长期有效,百科广告加上之后长期稳定有效,且不产生后续费用 ………   那么,百度百科创建词条工具可以提供哪些帮助呢? 1.创建百科词条,如品牌名、网站名、产品词、人名、公司名… 2.修改百科词条,在原有词条中加入您的内容,如广告内容、名片… 3.删除百科词条,删除不利词条内容或整个词条(不推荐使用) 4.百科内容撰写,根据客户要求编写词条内容 如有其他需求可及时与客服人员沟通,QQ:120962274 百度百科创建词条网站:www.uducn.com   百度百科创建词条常见错误: 1.修改词条原因不明确。例如:修改原因为“编辑词条”“不具体”“更完善”“更具体”等。 2.修改词条原因错误。例如:修改内容是添加图片,填写的修改原因为添加链接。 3.修改词条原因未能明确说明修改的具体区域。例如:修改了词条中的内容并添加了链接,修改原因应写明:添加内容以及链接,必须指出修改或删除的错误内容,并给出具体理由;只修改错别字,必须指明具体的错字;若您修改了表格中的内容,必须明确指出您修改了表格中的哪部分内容。 4.修改词条不可完全删除原词条的内容,可以选择性删除修改原词条内容。 5.编辑者误将角标误添加在段首,或误添加在了完整段落句号之前。   百度百科创建词条时请注意您的词条中不要有违背“百科原则”的内容,否则词条将被编辑删除,并扣除20分,情节严重者,“百科”有权对其做出关闭部分权限、暂停直至删除其帐号等处罚。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值