【POJ - 3422】Kaka's Matrix Travels 【拆点费用流+经典建图】

本文介绍了一道使用矩阵图论解决的最大权流问题,通过拆点的方式将原问题转化为寻找从起点到终点的最大权流,利用SPFA算法进行求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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
题意 :一个矩阵,每个位置上有点权,让你现在从左上角运动到右下角 走k次,每个点可以走多次,但是每个位置的点权只可以获得一次。问k次之后得到的最大的值。

分析 :我喜欢这样用矩阵来建图的题,很有感觉。HDU的这个题的变形
这两个题目,我都是用的拆点,拆的两个点之间的边来表示经过这个点获得的分数。
下面看图【图画的有点乱,TAT】
这里写图片描述
总共需要将n*n个点进行拆点,图中我假设从起点到终点之间只有一个点。
S->1的边流量为k,表明可以走k次旅行,费用为0。
1 -> 1’ 2 -> 2’ 等等即图中的绿色的边,其流量为1,费用为1 . 2. 等等位置的权值, 意思就是如果要经过这个边就 表明是获得这个位置的分数,流量限制为1表示只能够获得一次分数。
1->2 边流量为inf,费用为0,表明可以从1走到2但是不获得分数。
2->3同上理。
1’-> 2 流量为inf,费用为0,表明获得完1分数之后仍然可以走到2这个点。
2 ’ -> 3 同上理。
3->T 和3’->T 流量都为inf,费用为0,分别表明可以不获得分数到达终点的旅行,获得分数仍然可以到达终点的旅行。
一条流就代表一个旅行。
代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
#define  LL long long
#define fread() freopen("in.txt","r",stdin)
#define fwrite() freopen("out.txt","w",stdout)
#define CLOSE() ios_base::sync_with_stdio(false)

const int MAXN = 6000+11;
const int MAXM = 500000+11;
const int mod = 1e9+7;
const int inf = 0x3f3f3f3f;

struct Edge {
    int from,to,next,cap,flow,cost;
}edge[MAXM];
int head[MAXN],top;
void init(){
    memset(head,-1,sizeof(head));
    top=0;
} 
void addedge(int a,int b,int w,int c){
    edge[top].from=a,edge[top].to=b,edge[top].cap=w,edge[top].flow=0,edge[top].cost=c,edge[top].next=head[a];
    head[a]=top++;
    edge[top].from=b,edge[top].to=a,edge[top].cap=0,edge[top].flow=0,edge[top].cost=-c,edge[top].next=head[b];
    head[b]=top++;
}
int n,k,m;
int S,T;
int mp[55][55];
void getmap(){
     S=0,T=n*m*2+1;
     int temp=n*m;
     for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&mp[i][j]);
         }
     }
     addedge(S,1,k,0);
     for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            addedge((i-1)*n+j,(i-1)*n+j+temp,1,-mp[i][j]);//自己和对称的自己的边
            int nx=i+1;int ny=j;
            if(nx<1||ny<1||nx>n||ny>m) ;
            else {
                addedge(((i-1)*n+j),(nx-1)*n+ny,inf,0);// 不获得分数也可以到达的路
                addedge(((i-1)*n+j)+temp,(nx-1)*n+ny,inf,0);//获得分数仍然可以到达的路
             }
            nx=i;ny=j+1;
            if(nx<1||ny<1||nx>n||ny>m)  ;
            else {
                addedge(((i-1)*n+j),(nx-1)*n+ny,inf,0);
                addedge(((i-1)*n+j)+temp,(nx-1)*n+ny,inf,0);
             }
         }
     }
     addedge(n*m,T,inf,0);  //不获得分数也可以到达终点的路
     addedge(n*m+temp,T,inf,0);/、获得分数也可以到达终点的路
}

bool vis[MAXN];int dis[MAXN];
int pre[MAXN];
bool spfa(int s,int e){
    queue<int>Q;
    memset(vis,0,sizeof(vis));
    memset(dis,0x3f,sizeof(dis));
    memset(pre,-1,sizeof(pre));
    dis[s]=0;vis[s]=1;Q.push(s);
    while(!Q.empty()){
        int now=Q.front();Q.pop();vis[now]=0;
        for(int i=head[now];i!=-1;i=edge[i].next){
            Edge  e=edge[i];
            if(e.cap>e.flow&&dis[e.to]>dis[now]+e.cost){
                dis[e.to]=dis[now]+e.cost;
                pre[e.to]=i;
                if(!vis[e.to]){
                    vis[e.to]=1;
                    Q.push(e.to);
                }
            }
        }
    }
    return pre[e]!=-1;
}
int MCMF(int s,int t,int &cost,int &flow){
    flow=cost=0;
    while(spfa(s,t)){
        int Min=inf;
        for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
            Min=min(Min,edge[i].cap-edge[i].flow);
         }
         for(int i=pre[t];i!=-1;i=pre[edge[i^1].to]){
             edge[i].flow+=Min;
             edge[i^1].flow-=Min;
             cost+=edge[i].cost*Min;
         }
         flow+=Min;
     }
}
int main(){
    CLOSE();
//  fread();
//  fwrite();
    while(scanf("%d%d",&n,&k)!=EOF){
        init(); m=n;
        getmap();
        int flow,ans;
        MCMF(S,T,ans,flow);
        //printf("%d\n",flow);
        printf("%d\n",-ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值