题目:Link
网络流 - 费用流
一道比较好建模的题,重点是怎样让每个数只被取一次
控制每个点被走的次数,我们可以很容易想到拆点
对于每个点,我们在入点和出点之间连两条边,一条流量为
1
1
1,费用为该格子的权值;另一条流量为
∞
\infty
∞,费用为
0
0
0。除此之外,每个点还要分别向右边和下面的连边,流量为
∞
\infty
∞,费用为
0
0
0
最后,超级源点 s s s 向点 ( 1 , 1 ) (1,1) (1,1) 连边,且 ( n , n ) (n,n) (n,n) 向超级汇点 t t t 连边,流量为 k k k,费用为 0 0 0
你也许会想到(像我一样):如果在跑费用流时,先跑了 入点和出点之间的,流量为
∞
\infty
∞ 的边(也就是第一次到达方格时没有取数)
然而,这种情况是不可能的。因为最大费用流最大流是以费用为边权求最长路,所以一定会挑费用高的走,就不可能出现以上情况了
代码:
#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
using namespace std;
const int Maxn=5000+20,Maxm=30000+10,inf=0x3f3f3f3f;
int nxt[Maxm],to[Maxm],flow[Maxm],cost[Maxm];
int head[Maxn],dis[Maxn],pre[Maxn],cur[Maxn];
bool vis[Maxn];
int n,m,k,s,t,ans;
int edgecnt=1;
inline int read()
{
int s=0,w=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
return s*w;
}
inline void add(int x,int y,int v,int c)
{
++edgecnt;
nxt[edgecnt]=head[x];
to[edgecnt]=y;
flow[edgecnt]=v;
cost[edgecnt]=c;
head[x]=edgecnt;
}
inline int id(int x,int y)
{
return (x-1)*m+y;
}
bool spfa()
{
fill(dis+1,dis+4+(n<<1),-inf);
fill(cur+1,cur+4+(n<<1),inf);
queue <int> q;
vis[s]=1,dis[s]=0,q.push(s);
while(q.size())
{
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(!flow[i])continue;
if(dis[y]<dis[x]+cost[i])
{
dis[y]=dis[x]+cost[i];
pre[y]=i,cur[y]=min(cur[x],flow[i]);
if(!vis[y])vis[y]=1,q.push(y);
}
}
}
if(dis[t]==-inf)return 0;
return 1;
}
void upd()
{
int x=t;
while(x!=s && x)
{
int i=pre[x];
flow[i]-=cur[t];
flow[i^1]+=cur[t];
x=to[i^1];
}
ans+=cur[t]*dis[t];
}
int main()
{
// freopen("in.txt","r",stdin);
m=read(),k=read();
n=m*m,s=(n<<1|1) ,t=s+1;
add(s,id(1,1),k,0);
add(id(1,1),s,0,0);
add(id(m,m)+n,t,k,0);
add(t,id(m,m)+n,0,0);
for(int i=1;i<=m;++i)
{
for(int j=1;j<=m;++j)
{
int tmp=read();
int x=id(i,j);
add(x,x+n,1,tmp);
add(x+n,x,0,-tmp);
add(x,x+n,inf,0);
add(x+n,x,0,0);
if(i!=m)
{
add(x+n,id(i+1,j),inf,0);
add(id(i+1,j),x+n,0,0);
}
if(j!=m)
{
add(x+n,id(i,j+1),inf,0);
add(id(i,j+1),x+n,0,0);
}
}
}
while(spfa())upd();
printf("%d\n",ans);
return 0;
}
本文介绍了一种利用网络流中的费用流算法解决特定问题的建模方法,通过拆点策略确保每个数仅被选取一次。文章详细阐述了如何设置节点与边的流量及费用,以实现最优路径的选择,避免重复计数,并附带完整代码示例。

480

被折叠的 条评论
为什么被折叠?



