/*
费用流,这道题的关键在于建图,看了一下大牛的博客,想了这道题
总结一下思路;
首先对于这种单一走路的题,一定要有拆点的思路对于这道题关建就在于
拆点,首先让我们想一下,怎么让一个点只走一下呢,这就是插电的精髓
然后加一条边,容量为 1,费用为当前数值的一条边,然后把这个点的右方
下方的点加一条边,左上角顶点作为原点右下角顶点作为汇点,求一遍费用流
就行。。。。
*/
#include<iostream>
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 100000;
const int inf = 0x777777;
struct {
int head;
}H[maxn];
struct {
int u,v,next,cap,cost;
}E[10*maxn];
int top;
void add(int u,int v,int cap,int cost) {
E[top].v = v;E[top].cap = cap;
E[top].cost = cost;
E[top].next = H[u].head;
H[u].head = top++;
E[top].v = u;
E[top].cap = 0;
E[top].cost = - cost;
E[top].next = H[v].head;
H[v].head = top++;
}
int d[maxn];
int p[maxn];
int vis[maxn];
int path[maxn];
int max_flow,ans;
bool spfa(int s,int t,int n)
{
for(int i=1;i<=n;i++)
d[i] = -inf;
d[s] = 0;
memset(vis,0,sizeof(vis));
queue <int> q;
q.push(s);vis[s] = 1;
while(!q.empty())
{
int x = q.front();q.pop();vis[x] = 0;
//cout<<x<<endl;
for(int i=H[x].head;i!=-1;i = E[i].next)
{
if(E[i].cap > 0 && d[E[i].v] < d[x]+E[i].cost)
{
d[E[i].v] = d[x]+E[i].cost;
path[E[i].v] = i; //这个地方很是关键,记录当前边的编号
p[E[i].v] = x;
if(!vis[E[i].v])
{
vis[E[i].v] = 1;
q.push(E[i].v);
}
}
}
}
if(d[t] == -inf) return false;
int u = t,sum = inf;
while (u!=s) {
sum = min(E[path[u]].cap,sum);
u = p[u];
}
u = t;
max_flow += sum;
while(u != s) { // 更新正反流量
E[path[u]].cap -= sum;
E[path[u]^1].cap += sum;
ans += E[path[u]].cost * sum;
u = p[u];
}
return true;
}
void init()
{
memset(H,-1,sizeof(H));
memset(E,-1,sizeof(E));
top = 0;
ans = 0;
max_flow = 0;
}
int main()
{
int n,m,k;
int u,v,cost,cap,num;
int T;
scanf("%d",&T);
while(T --)
{
init();
scanf("%d%d",&m,&n);
add(0,1,2,0);
add(2*(m*n-1),2*(m*n-1)+1,2,0);
for(int i=0;i<m;i++)
for(int j=0;j<n;j++)
{
scanf("%d",&num);
if(i > 0) add (2*((i-1)*n+j)+1,2*(i*n+j) ,1,0);
if(j > 0) add (2*(i*n+j-1)+1,2*(i*n+j),1,0);
if(!(i==0&&j==0)&&!(i==m-1&&j==n-1))
add(2*(i*n+j),2*(i*n+j)+1,1,num);
}
while(spfa(0,2*(m*n-1)+1,2*(n*m-1)+1));
cout<<ans<<endl;
}
}
nyoj 传纸条(一)
最新推荐文章于 2022-08-12 19:10:48 发布