POJ 3422 Kaka's Matrix Travels

题目链接:ヾ(≧∇≦*)ゝ

大致题意:给你一个\(n*n\)的矩阵从\((1,1)\)出发,到\((n,n)\)结束,只能走右边或左边。每个点在被走过之后权值变成0,问走\(k\)次后,能获得的最大值总和是多少

Solution:

看数据范围,\(n\le50,0\le k \le10\),再结合题目,一下子就能想到费用流

那么,该如何建图呢?

我们看一下限制:

首先,每一个点只能贡献一次价值,如何来解决这个问题呢?

拆点。把每个点分为入点和出点,入点和出点之间的费用为该点权值,容量为1,这样就能够解决这条限制。

然后,再把每一个点的出点与它右边的点的入点和下面的点的入点连边,费用为0,容量为k

再是能够走k次,我们建立一个超级源点和超级汇点,拿出发点的入点与源点连边,容量为k,费用为0,结束点的出点与汇点连边,容量为k,费用为0

不过,仅仅这样是不行的。按照上述的建图,我们发现它只能应对\(k\le2\)的情况,最多跑完两次最大费用流,它就会无路可走,所以我们还需要连一些边。

对于每一个点,将它的出点与它右边点的的出点,下面点的出点都连一条容量为k,费用为0的边。

这是什么意思呢?这条边代表只是经过这个点,而不取该点的权值。

最后跑最大费用流,拿答案减去多加的出发点和结束点的权值,就是答案了

不过,当\(k=0\)时,需要特判一下,直接输出0。(博主就是因为忘记特判而狂WA不止啊...)

Code:

#include<queue>
#include<cstdio>
#include<ctype.h>
#include<cstring>
#include<algorithm>
#define N 30001
#define inf 0x8f
using namespace std;
int S,T,n,k,cnt=1,tot;
int head[N],mp[51][51];
struct Edge{int nxt,to,v,w;}edge[N];
void ins(int x,int y,int z,int w){
    edge[++cnt].nxt=head[x];
    edge[cnt].to=y;edge[cnt].v=z;
    edge[cnt].w=w;head[x]=cnt;
}
namespace Network_Flow{
    queue<int> q;
    int maxcost,delta;
    int dis[N],vis[N],pre[N];
    int spfa(){
        pre[T]=0;delta=inf;
        memset(vis,0,sizeof(vis));
        memset(dis,255,sizeof(dis));
        q.push(S);vis[S]=1;dis[S]=0;
        while(!q.empty()){
            int x=q.front();q.pop();vis[x]=0;
            for(int i=head[x];i;i=edge[i].nxt){
                int y=edge[i].to;
                if(edge[i].v&&dis[x]+edge[i].w>dis[y]){
                    dis[y]=dis[x]+edge[i].w;
                    delta=min(delta,edge[i].v);
                    pre[y]=i;if(!vis[y]) q.push(y),vis[y]=1;
                }
            }
        }
        return pre[T];
    }
    void update(){
        int x=T;
        while(x!=1){
            int i=pre[x];
            edge[i].v-=delta;
            edge[i^1].v+=delta;
            x=edge[i^1].to;
        }
        maxcost+=dis[T]*delta;
    }
    void Edmond_Karp(){
        while(spfa()) update();
        printf("%d\n",maxcost-(k-1)*(mp[1][1]+mp[n][n]));
    }
}
int num(int i,int j){return (i-1)*n*2+j*2;}
int read(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    while(isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
int main(){
    n=read(),k=read();S=n*n*2+1,T=S+1;
    using namespace Network_Flow;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            mp[i][j]=read();tot+=2;
            if((i==1&&j==1)||(i==n&&j==n)){
                ins(tot-1,tot,k,mp[i][j]);
                ins(tot,tot-1,0,-mp[i][j]);
                continue;
            }
            ins(tot-1,tot,1,mp[i][j]);
            ins(tot,tot-1,0,-mp[i][j]);
        }
    if(!k){printf("0");return 0;}
    ins(S,1,inf,0);ins(1,S,0,0);
    ins(tot,T,inf,0);ins(T,tot,0,0);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            if(i<n){
                ins(num(i,j),num(i+1,j)-1,k,0);
                ins(num(i+1,j)-1,num(i,j),0,0);
                ins(num(i,j),num(i+1,j),k,0);
                ins(num(i+1,j),num(i,j),0,0);
            }
            if(j<n){
                ins(num(i,j),num(i,j+1)-1,k,0);
                ins(num(i,j+1)-1,num(i,j),0,0);
                ins(num(i,j),num(i,j+1),k,0);
                ins(num(i,j+1),num(i,j),0,0);
            }
        }
    Edmond_Karp();
    return 0;
}
posted @ 2019-01-29 13:05  DQY_dqy  阅读(136)  评论(0编辑  收藏  举报