现在的位置: 首页 > 综合 > 正文

POJ–3422–Kaka’s Matrix Travels【最小费用最大流+拆点】

2018年01月12日 ⁄ 综合 ⁄ 共 3433字 ⁄ 字号 评论关闭

链接:http://poj.org/problem?id=3422

卡卡

题意:卡卡的矩阵之旅,有一个n*n的矩阵,卡卡要从左上角走到右下角,每次他只能往右或往下走,卡卡可以走k遍这个矩阵,每个点有一个num值,卡卡走到这里可以获得num点,一个点只能获得一次num值,问卡卡走完k遍后身上num值最大可以是多少?

思路:其实看到这题时没思路,图论书上说了建图的方式,但没有说为什么,我不解,网上搜了一下解题报告,百度了两页,我看到的博客都是写了如何建图,但没有写为什么要这么建。。我觉得我真是弱渣,别人看了如何建图就立即能明白,我看了如何建图还是没明白,实在找不到讲解就自己想吧!自己想其实也不算难,理解了为什么拆点这道题其实比较简单,下面是我想完之后写的:

首先,贪心是肯定不行的,贪心的想法是每次取得尽量大的sum值,如果只走一遍这么做也需要考虑走过之后的情况,贪心勉强可以做,DP做更好。但是现在要走K遍,每一次走的路都会对之后的产生影响,因为每次只能向两个方向走,需要合理的规划这K次的走法,我DP很弱,不知道DP能不能做。
再按照网络流的想法来想:要走K遍,并且每个位置都有一个值num,把num来当做流量是不可取的,因为这个值是最终的答案,是要累加起来的,而流量则取路径中最大的那一个,不是累加。
那么把什么当做流量?可以把K值当做最终的最大流,超级源点、超级汇点的弧流量都是K,这就限定了只走K遍的情况,每次走的一整条路算作一个流量,总流量为K。
那num用来做什么?num就成了网络中的限制条件,当最大流相同时选尽量大的num值,其实就是最小费用最大流,只不过现在不是取最小费用,而是取最大费用。
现在再想矩阵中该如何建图,容易想到每一点向它右边和下边连一条弧,费用可以选用这个点的num值,但容量不好取。试想,如果容量取为INF,那卡卡可以每次都走总num值最大的那条路,走K遍,因为矩阵中没有对走的次数进行限制,只在超级源汇做了限制,但一个点的num值只能取一次,这么走等于一个点的num值取了K次。那容量应该取为1吗?也不对,取1限制了一个值只会取到一次,但问题随之而来:a点到b点如果有路,这条路也只能走一次,题意中没有这个规定,题意是说一个点可以走很多次,但是值只能取一次。容量取K和取INF是一样的,也不可取。
为了解决一个值只能取一次,但一个点可以走多次的问题,就有了网上广为流传的办法:拆点

拆点了之后a点变为a'和a'',a'→a''表示取了a点的num值,这条弧的容量为1,费用为num,再连一条端点一样的弧,容量INF,费用为0,这样保证了一个点的值只取一次。取完后向a点可以走向的b点行进,b点也同样拆成了b'和b'',a''到b'连弧,容量为INF,表示一个点可以走很多次,这样就很好的解决了刚才的问题。
其实这种题应该多做几道就能想到拆点了。

建图:上一段差不多都说了,矩阵中的建图就如上面所说,没有费用的弧只要保证容量不比K小就行了。超级源点连向矩阵左上角,容量为k,费用0,矩阵最右下角的点a''连向超级汇点,容量k,费用0,建图就是这样。

理解了之后搞了很久才AC,因为在SPFA里初始化dist数组范围小了,程序陷入了死循环。

还有一点,我输出的ans是全局变量,我在主函数里又定义了一个ans,程序优先使用局部变量,无限输出0。。我debug了超久才发现。。

#include<cstring>
#include<string>
#include<fstream>
#include<iostream>
#include<iomanip>
#include<cstdio>
#include<cctype>
#include<algorithm>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<stack>
#include<ctime>
#include<cstdlib>
#include<functional>
#include<cmath>
using namespace std;
#define PI acos(-1.0)
#define MAXN 500100
#define eps 1e-7
#define INF 0x7FFFFFFF
#define LLINF 0x7FFFFFFFFFFFFFFF
#define seed 131
#define mod 1000000007
#define ll long long
#define ull unsigned ll
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1

struct node{
    int u,v,w,cost,next;
}edge[MAXN];
int head[5200],dist[5200],pree[5200],vis[5200];
int n,m,k,cnt,src,sink,ans,nn;
int a[60][60];
void add_edge(int a,int b,int c,int d){
    edge[cnt].v = b;
    edge[cnt].w = c;
    edge[cnt].cost = d;
    edge[cnt].next = head[a];
    head[a] = cnt++;
}
void build_graph(){
    int i,j;
    cnt = 0;
    memset(head,-1,sizeof(head));
    src = 0;
    sink = n * n * 2 + 1;
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            int tt = (i-1)*n+j;
            add_edge(tt,tt+nn,1,-a[i][j]);
            add_edge(tt+nn,tt,0,a[i][j]);
            add_edge(tt,tt+nn,k,0);
            add_edge(tt+nn,tt,0,0);
            if(i+1<=n){
                add_edge(tt+nn,tt+n,k,0);
                add_edge(tt+n,tt+nn,0,0);
            }
            if(j+1<=n){
                add_edge(tt+nn,tt+1,k,0);
                add_edge(tt+1,tt+nn,0,0);
            }
        }
    }
    add_edge(src,1,k,0);
    add_edge(1,src,0,0);
    add_edge(nn*2,sink,k,0);
    add_edge(sink,nn*2,0,0);
}
bool spfa(){
    int i,j;
    queue<int>q;
    q.push(src);
    memset(vis,0,sizeof(vis));
    int lll = nn*2+5;
    for(i=0;i<=lll;i++)   dist[i] = INF;
    dist[src] = 0;
    vis[src] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(i=head[u];i!=-1;i=edge[i].next){
            if(edge[i].w!=0&&dist[u]+edge[i].cost<dist[edge[i].v]){
                dist[edge[i].v] = dist[u] + edge[i].cost;
                pree[edge[i].v] = i;
                if(!vis[edge[i].v]){
                    vis[edge[i].v] = 1;
                    q.push(edge[i].v);
                }
            }
        }
    }
    if(dist[sink]!=INF)  return true;
    return false;
}
int augment(){
    int i,j;
    int delta = INF;
    for(i=sink;i!=src;i=edge[j^1].v){
        j = pree[i];
        delta = min(delta,edge[j].w);
    }
    for(i=sink;i!=src;i=edge[j^1].v){
        j = pree[i];
        edge[j].w -= delta;
        edge[j^1].w += delta;
        ans += edge[j].cost;
    }
    return delta;
}

int main(){
    int i,j;
    int b,c,d;
    ans = 0;
    scanf("%d%d",&n,&k);
    nn = n * n;
    for(i=1;i<=n;i++){
        for(j=1;j<=n;j++){
            scanf("%d",&a[i][j]);
        }
    }
    build_graph();
    while(spfa())   augment();
    printf("%d\n",-ans);
    return 0;
}

抱歉!评论已关闭.