标签:front lan turn 取数 加强 clu i+1 mat bool
因为一个点的贡献只能算一次,把点拆成2个点即可,连一条流量为\(1\),费用为这个点的值,然后再连一条流量为\(+\infty\),费用为\(0\)的边,接下来对于每个点向它下方和它右方的点连一条流量为\(+\infty\),费用为\(0\)的边即可。算最大费用,边权取反。
#include <bits/stdc++.h>
int n,k,S,T,mincost;
int a[111][111];
int head[1000000],tot=1;
struct edge{int to,nxt,flow,cost;}e[1000000];
int dis[6600],vis[6600],flow[6600],pren[6600],pree[6600];
std::queue<int>q;
int GN(int x,int y){
return x*n-n+y;
}
void add(int x,int y,int flow,int w){
e[++tot]={y,head[x],flow,w};
head[x]=tot;
e[++tot]={x,head[y],0,-w};
head[y]=tot;
}
bool spfa(){
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
memset(flow,0x3f,sizeof flow);
q.push(S);
vis[S]=1;
dis[S]=0;
while(!q.empty()){
int x=q.front();
q.pop();
vis[x]=0;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;
vis[x]=0;
if(e[i].flow&&dis[y]>dis[x]+e[i].cost){
dis[y]=dis[x]+e[i].cost;
flow[y]=std::min(flow[x],e[i].flow);
pren[y]=x;
pree[y]=i;
if(!vis[y]){
vis[y]=1;
q.push(y);
}
}
}
}
return dis[T]!=0x3f3f3f3f;
}
void dinic(){
while(spfa()){
mincost-=dis[T]*flow[T];
for(int i=T;i!=S;i=pren[i]){
e[pree[i]].flow-=flow[T];
e[pree[i]^1].flow+=flow[T];
}
}
}
main(){
scanf("%d%d",&n,&k);
S=2*n*n+1,T=S+1;
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;++i)
for(int j=1;j<=n;++j){
add(GN(i,j),GN(i,j)+n*n,1,-a[i][j]);
add(GN(i,j),GN(i,j)+n*n,0x3f3f3f3f,0);
if(i<n)add(GN(i,j)+n*n,GN(i+1,j),k,0);
if(j<n)add(GN(i,j)+n*n,GN(i,j+1),k,0);
}
add(S,GN(1,1),k,0);
add(GN(n,n)+n*n,T,k,0);
n=T;
dinic();
printf("%d\n",mincost);
return 0;
}
标签:front lan turn 取数 加强 clu i+1 mat bool
原文地址:https://www.cnblogs.com/Skylight/p/12958524.html