POJ 3422 Kaka's Matrix Travels(费用流)
http://poj.org/problem?id=3422
题意:
给一个N*N的方阵,从[1,1]到[n,n]走K次,走过每个方格加上上面的数(每个方格初始都有一个非负数),然后这个格上面的数变为0。求可取得的最大的值。
分析:
其实把每个网格看成有向图中的一个点,每两个相邻的网格之间有一条边,那么我们就是等于要找K次最小费用增广路径即可(就是一个求最小费用最大流的过程). 建图如下:
源点s编号0,n*n个网格每个网格分成两个点i和i+n*n, 汇点t编号为n*n*2+1.
从源点s到1号节点有边(s , 1, K)
从每个网格到自己有边(i, i+n*n, 1, -cost) 和(i, i+n*n, INF, 0) (注意这里的-cost,因为原题要我们求最大值)
从每个网格i到它的右或下那个网格j有边(i+n*n, j, INF, 0)
从最右下角到汇点t有边(2*n*n, t, INF, 0)
最终我们求得的最小费用的绝对值就是权值最大值.
AC代码:最小费用取绝对值解法
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define INF 1e9
using namespace std;
const int maxn= 51*51*2+10;
struct Edge
{
int from,to,cap,flow,cost;
Edge(){}
Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF
{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
bool inq[maxn];
int d[maxn];
int a[maxn];
int p[maxn];
void init(int n,int s,int t)
{
this->n=n, this->s=s, this->t=t;
edges.clear();
for(int i=0;i<n;++i) G[i].clear();
}
void AddEdge(int from,int to,int cap,int cost)
{
edges.push_back(Edge(from,to,cap,0,cost));
edges.push_back(Edge(to,from,0,0,-cost));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int &flow,int &cost)
{
memset(inq,0,sizeof(inq));
for(int i=0;i<n;++i) d[i]=INF;
queue<int> Q;
d[s]=0,inq[s]=true, Q.push(s),a[s]=INF,p[s]=0;
while(!Q.empty())
{
int u =Q.front(); Q.pop();
inq[u]=false;
for(int i=0;i<G[u].size();++i)
{
Edge &e=edges[G[u][i]];
if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
a[e.to]=min(a[u],e.cap-e.flow);
p[e.to]=G[u][i];
if(!inq[e.to]){inq[e.to]=true; Q.push(e.to);}
}
}
}
if(d[t]==INF) return false;
flow += a[t];
cost += a[t]*d[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow +=a[t];
edges[p[u]^1].flow -=a[t];
u=edges[p[u]].from;
}
return true;
}
int solve()
{
int flow=0,cost=0;
while(BellmanFord(flow,cost));
return cost;
}
}MM;
int mp[55][55];
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)==2)
{
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&mp[i][j]);
int src=0,dst=n*n*2+1;
MM.init(n*n*2+2,src,dst);
MM.AddEdge(src,1,k,0);
MM.AddEdge(n*n*2,dst,INF,0);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
int id=(i-1)*n+j;
MM.AddEdge(id,id+n*n,1,-mp[i][j]);
MM.AddEdge(id,id+n*n,INF,0);
if(j+1<=n) MM.AddEdge(id+n*n,id+1,INF,0);
//这里如果写if(id+1<=n*n)是错的,最后一列虽然能+1,但是无后继
if(i+1<=n) MM.AddEdge(id+n*n,id+n,INF,0);
}
printf("%d\n",-MM.solve());
}
return 0;
}
AC代码:直接求最大费用路径解法,两个程序的不同点在下面已经标出来了,注意细节
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#define INF 1e9
using namespace std;
const int maxn= 51*51*2+10;
struct Edge
{
int from,to,cap,flow,cost;
Edge(){}
Edge(int from,int to,int cap,int flow,int cost):from(from),to(to),cap(cap),flow(flow),cost(cost){}
};
struct MCMF
{
int n,m,s,t;
vector<Edge> edges;
vector<int> G[maxn];
bool inq[maxn];
int d[maxn];
int a[maxn];
int p[maxn];
void init(int n,int s,int t)
{
this->n=n, this->s=s, this->t=t;
edges.clear();
for(int i=0;i<n;++i) G[i].clear();
}
void AddEdge(int from,int to,int cap,int cost)
{
edges.push_back(Edge(from,to,cap,0,cost));
edges.push_back(Edge(to,from,0,0,-cost));
m = edges.size();
G[from].push_back(m-2);
G[to].push_back(m-1);
}
bool BellmanFord(int &flow,int &cost)
{
memset(inq,0,sizeof(inq));
for(int i=0;i<n;++i) d[i]=-1; //修改1
queue<int> Q;
d[s]=0,inq[s]=true, Q.push(s),a[s]=INF,p[s]=0;
while(!Q.empty())
{
int u =Q.front(); Q.pop();
inq[u]=false;
for(int i=0;i<G[u].size();++i)
{
Edge &e=edges[G[u][i]];
if(e.cap>e.flow && d[e.to]<d[u]+e.cost)//修改2
{
d[e.to]=d[u]+e.cost;
a[e.to]=min(a[u],e.cap-e.flow);
p[e.to]=G[u][i];
if(!inq[e.to]){inq[e.to]=true; Q.push(e.to);}
}
}
}
if(d[t]==-1) return false;//修改3
flow += a[t];
cost += a[t]*d[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow +=a[t];
edges[p[u]^1].flow -=a[t];
u=edges[p[u]].from;
}
return true;
}
int solve()
{
int flow=0,cost=0;
while(BellmanFord(flow,cost));
return cost;
}
}MM;
int mp[55][55];
int main()
{
int n,k;
while(scanf("%d%d",&n,&k)==2)
{
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&mp[i][j]);
int src=0,dst=n*n*2+1;
MM.init(n*n*2+2,src,dst);
MM.AddEdge(src,1,k,0);
MM.AddEdge(n*n*2,dst,INF,0);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
{
int id=(i-1)*n+j;
MM.AddEdge(id,id+n*n,1,mp[i][j]);//修改4
MM.AddEdge(id,id+n*n,INF,0);
if(j+1<=n) MM.AddEdge(id+n*n,id+1,INF,0);
//这里如果写if(id+1<=n*n)是错的,最后一列虽然能+1,但是无后继
if(i+1<=n) MM.AddEdge(id+n*n,id+n,INF,0);
}
printf("%d\n",MM.solve());//修改5
}
return 0;
}