给一张网格图,图上有一些机器人,它们要到指定的地方,它们都只能往东和北移动,每条边都有价值,但是只能计算一次,问可以达到的最大价值。
因为只能计算一次,所以可以这样建边:
- 每个点往这个点北方的那个点连两条边,一条流量为 1 1 1,费用为价值,另一条流量 ∞ \infty ∞,费用为 0 0 0。
之后:
- 让源点和机器人出发的地方连边,流量为机器人个数,费用为 0 0 0。
- 机器人停下的地方向汇点连边,流量为机器人个数,费用为 0 0 0。
因为要最大费用,所以可以把边权取反。
c
o
d
e
:
code:
code:
#include <bits/stdc++.h>
int a,b;
int P,Q;
int mincost;
int S=1,T=2;
int dis[10000];
int vis[10000];
int flow[10000];
int pren[10000];
int pree[1000000];
int head[1000000],tot=1;
std::queue<int>q;
struct edge{
int to;
int nxt;
int flow;
int cost;
}e[1000000];
int get_num(int i,int j){
return 3+i*20+j;
}
void add(int x,int y,int flow,int cost){
e[++tot]={y,head[x],flow,cost};
head[x]=tot;
e[++tot]={x,head[y],0,-cost};
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;
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];
}
}
printf("%d\n",mincost);
}
main(){
scanf("%d%d",&a,&b);
scanf("%d%d",&P,&Q);
for(int i=0;i<=P;++i)
for(int j=0;j<Q;++j){
int x;
scanf("%d",&x);
add(get_num(i,j),get_num(i,j+1),1,-x);
add(get_num(i,j),get_num(i,j+1),0x3f3f3f3f,0);
}
for(int j=0;j<=Q;++j)
for(int i=0;i<P;++i){
int x;
scanf("%d",&x);
add(get_num(i,j),get_num(i+1,j),1,-x);
add(get_num(i,j),get_num(i+1,j),0x3f3f3f3f,0);
}
for(int i=1;i<=a;++i){
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
add(S,get_num(x,y),k,0);
}
for(int i=1;i<=b;++i){
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
add(get_num(x,y),T,k,0);
}
dinic();
return 0;
}