poj3422 Kaka's Matrix Travels

题面

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
简单来说就是有一个n*n的棋盘,每个点都有一个value,一开始sum为0,从左上角走到右下角,每次只能往下走或往右走,每次走到一个点sum都会加上那个点的value,并把那个点的value置零,问如果k次从左上角走到右下角,sum的总和最大是多少。

分析

首先,如果只走一次,那么这道题用动态规划无疑,但走k次,随着k的增加,动态规划的状态转移变得越来越复杂,维度也越来越高,这时候这道题就不能用动态规划求解了,正解便是网络流中的最大费用最大流(最大费用最大流直接把最小费用最大流里的边向大的松弛就行,dist数组记得初始化为-1)。
建图:
首先我们可以把棋盘中的点看作网络流的点,对于棋盘上每个点的权值,我们可以利用拆点的方式处理,把一个点拆成两个点a和b,这两个点间连一条权值为原点的value的边,容量为1,即只能通过一次,然后再连一条权值为0,容量为无穷的点,意味着通过一次之后还可以通过,只不过sum不会增加,a点连接原点的入边,b点连接原点的出边,以及再设置一个源点s和汇点t,源点到第一个点连接一条权值为0,容量为k的边,代表可以k次通过,同理,最后一个点和汇点之间连接一条权值为0的边,容量也为k。到此建图结束,接下来只需用dinic算法即可。
AC代码:

#include<iostream>
#include<queue>
#include<cstring>
#define rep(i,x,n) for(int i=x;i<n;i++)
#define per(i,x,n) for(int i=n-1;i>=x;i--)
using namespace std;
//head
const int maxn=1e5+6;
const int inf=1<<30;
typedef long long ll;
int n,k,cur=0,len,t,s,mp[1006][1006],head[maxn],flag[maxn],pre[maxn],load[maxn],dist[maxn];
struct Edge{int to,next,cost,flow;}edge[10*maxn];
void addedge(int i,int j,int v,int k)
{
    edge[cur].to=j;edge[cur].next=head[i];head[i]=cur;edge[cur].cost=v;edge[cur].flow=k;cur++;
    edge[cur].to=i;edge[cur].next=head[j];head[j]=cur;edge[cur].cost=-v;edge[cur].flow=0;cur++;
}
int spfa()
{
    memset(dist,-1,sizeof(dist));
    memset(flag,0,sizeof(flag));
    memset(pre,-1,sizeof(pre));
    memset(load,-1,sizeof(load));
    queue<int>q;
    q.push(0);
    dist[0]=0;
    flag[0]=1;
    while(!q.empty())
    {
        int x=q.front();q.pop();
        flag[x]=0;
        for(int i=head[x];i!=-1;i=edge[i].next)
        {
            if(edge[i].flow)
            {
                int nx=edge[i].to;
                if(edge[i].cost+dist[x]>dist[nx])
                {
                    dist[nx]=edge[i].cost+dist[x];
                    pre[nx]=x;
                    load[nx]=i;
                    if(!flag[nx])
                    {
                        flag[nx]=1;
                        q.push(nx);
                    }
                }
            }
        }
    }
    return dist[t]!=-1;
}
ll getmaxsum()
{
    ll ret=0;
    while(spfa())
    {
        int mn=inf;
       for(int u=t;pre[u]!=-1;u=pre[u])
        mn=min(edge[load[u]].flow,mn);
        for(int u=t;pre[u]!=-1;u=pre[u])
        edge[load[u]].flow-=mn,edge[load[u]^1].flow+=mn;
        ret+=mn*dist[t];
    }
    return ret;
}
int main()
{
     ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
     memset(head,-1,sizeof(head));
     cin>>n>>k;
     int tmp,tmp2,d,ri;len=2*n*n;t=len+1;
     rep(i,0,n)rep(j,1,n+1)cin>>mp[i][j];
     addedge(0,1,0,k);
     addedge(len,t,0,k);
     rep(i,0,n)
     rep(j,1,n+1)
     {
         tmp=2*(i*n+j)-1;
         tmp2=2*(i*n+j);
         addedge(tmp,tmp2,mp[i][j],1);
         addedge(tmp,tmp2,0,inf);
         d=tmp+2*n;
         ri=tmp+2;
         if(i!=n-1)addedge(tmp2,d,0,inf);
         if(j!=n)addedge(tmp2,ri,0,inf);
     }
     cout<<getmaxsum()<<endl;
     return 0;
}

版权声明:本文为原创文章,转载请标明出处。
https://blog.csdn.net/u014390156

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值