题意:一个n*n(n<=30)的矩阵,求从(1,1)出发到(n,n)的两条路径,满足除了起点和终点之外,两条路径不得有重复.求最大和
思路:
- MAXN开小了一直TLE
- 对于每一个点,可以接一条边到下方和右方
- 然而对于一个点,只能选择2次.则有流经该点的流<=1
- 但是原图不能直接构造
- 于是对每一个节点我们开影子节点(拆点),容量为1,这样就可以限流(限制次数了)
- 注意一下最大流的时候代价取反,对答案再取绝对值即可
#include <cstdio>
#include <cstring>
#include <queue>
#include <stack>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <iostream>
#define mAXn 2000+10
#define mAXm 30000+30
#define InF 0x3f3f3f3f
using namespace std;
struct Edge
{
int from, to, cap, flow, cost, next;
};
Edge edge[mAXm];
int head[mAXn], edgenum;
int pre[mAXn];//记录增广路径上 到达点i的边的编号
int dist[mAXn];
bool vis[mAXn];
int n, m;//点数 边数
int source, sink;//超级源点 超级汇点
void init()
{
edgenum = 0;
memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w, int c)
{
Edge E1 = {u, v, w, 0, c, head[u]};
edge[edgenum] = E1;
head[u] = edgenum++;
Edge E2 = {v, u, 0, 0, -c, head[v]};
edge[edgenum] = E2;
head[v] = edgenum++;
}
bool SPFA(int s, int t)//寻找花销最少的路径
{
//跑一遍SPFA 找s——t的最少花销路径 且该路径上每一条边不能满流
//若存在 说明可以继续增广,反之不能
queue<int> Q;
memset(dist, InF, sizeof(dist));
memset(vis, false, sizeof(vis));
memset(pre, -1, sizeof(pre));
dist[s] = 0;
vis[s] = true;
Q.push(s);
while(!Q.empty())
{
int u = Q.front();
Q.pop();
vis[u] = false;
for(int i = head[u]; i != -1; i = edge[i].next)
{
Edge E = edge[i];
if(dist[E.to] > dist[u] + E.cost && E.cap > E.flow)//可以松弛 且 没有满流
{
dist[E.to] = dist[u] + E.cost;
pre[E.to] = i;//记录前驱边 的编号
if(!vis[E.to])
{
vis[E.to] = true;
Q.push(E.to);
}
}
}
}
return pre[t] != -1;//可达返回true
}
void mCmF(int s, int t, int &cost, int &flow)
{
flow = 0;//总流量
cost = 0;//总费用
while(SPFA(s, t))//每次寻找花销最小的路径
{
int Min = InF;
//通过反向弧 在源点到汇点的最少花费路径 找最小增广流
for(int i = pre[t]; i != -1; i = pre[edge[i^1].to])
{
Edge E = edge[i];
Min = min(Min, E.cap - E.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 k;
int a[50][50];
int hs(int i,int j){
return (i-1)*n+j;
}
bool check(int i,int j){
if(i<=n&&j<=n) return true;
else return false;
}
void getmap(){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
source=0,sink=1+2*n*n;
addEdge(0,1,2,0);
addEdge(hs(1,1),hs(1,2),1,-a[1][2]);
addEdge(hs(1,1),hs(2,1),1,-a[2][1]);
addEdge(n*n,sink,2,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i==1&&j==1) continue;
if(i==n&&j==n) break;
int id=hs(i,j)+n*n;
addEdge(hs(i,j),id,1,0);
if(check(i,j+1)) addEdge(id,hs(i,j+1),1,-a[i][j+1]);
if(check(i+1,j)) addEdge(id,hs(i+1,j),1,-a[i+1][j]);
}
}
return ;
}
int main()
{
while(scanf("%d", &n)==1)
{
init();
getmap();//建图
int cost, flow;//最小费用 最大流
mCmF(source, sink, cost, flow);
if(flow<k) printf("-1\n");
else printf("%d\n", -cost-a[n][n]+a[1][1]);//最小费用 最大流
// printf("%d\n", flow);//最小费用 最大流
}
return 0;
}