题目描述

深海资源考察探险队的潜艇将到达深海的海底进行科学考察。
潜艇内有多个深海机器人。潜艇到达深海海底后,深海机器人将离开潜艇向预定目标移动。
深海机器人在移动中还必须沿途采集海底生物标本。沿途生物标本由最先遇到它的深海机器人完成采集。
每条预定路径上的生物标本的价值是已知的,而且生物标本只能被采集一次。
本题限定深海机器人只能从其出发位置沿着向北或向东的方向移动,而且多个深海机器人可以在同一时间占据同一位置。
用一个 p×qp\times q 网格表示深海机器人的可移动位置。西南角的坐标为 (0,0)(0,0),东北角的坐标为 (p,q)(p,q)
深海机器人问题.png
给定每个深海机器人的出发位置和目标位置,以及每条网格边上生物标本的价值。
计算深海机器人的最优移动方案, 使深海机器人到达目的地后,采集到的生物标本的总价值最高。

输入格式

文件的第 11 行为深海机器人的出发位置数 aa,和目的地数 bb
22 行为 ppqq 的值。
接下来的 p+1p+1 行,每行有 qq 个正整数,表示向东移动路径上生物标本的价值,行数据依从南到北方向排列。
再接下来的 q+1q+1 行,每行有 pp 个正整数,表示向北移动路径上生物标本的价值,行数据依从西到东方向排列。
接下来的 aa 行,每行有 33 个正整数 k,x,yk,x,y 表示有 kk 个深海机器人从 (x,y)(x,y) 位置坐标出发。
再接下来的 bb 行,每行有 33 个正整数 r,x,yr,x,y ,表示有 rr 个深海机器人可选择 (x,y)(x,y) 位置坐标作为目的地。
aa 行和 bb 行输入时横纵坐标要反过来。

输出格式

输出采集到的生物标本的最高总价值。

输入输出样例

输入 #1

1 1
2 2
1 2
3 4
5 6
7 2
8 10
9 3
2 0 0
2 2 2

输出 #1

42

说明/提示

1p,q151\leqslant p,q\leqslant151a41\leqslant a\leqslant 41b61\leqslant b\leqslant 6

分析

给出了每条边的价值,要使多条路线的价值和最大,显然可以套用最大费用最大流模型。
不妨将 p,qp,q 都加一,坐标原点设为 (1,1)(1,1),方便表示坐标为 (x,y)(x,y) 的点的编号。定义坐标为 (x,y)(x,y) 的点的编号为 ID(x,y)=p(x1)+yID(x,y)=p(x-1)+y
设源点 ss,汇点 tt。从 ss 向每个机器人开始的地方连容量为机器人数量,费用为 00 的边。从每个目的地向 tt连容量为 rr,费用为 00 的边。这就限定了最大流为机器人的总数。
值得思考的是相邻点之间的连边。对于一个点 (x,y)(x,y),其相邻点为 (x+1,y)(x+1,y)(x,y+1)(x,y+1)。由于生物标本只能采集一次,可以从 (x,y)(x,y) 向其相邻点连边,容量为 11,费用为采集生物标本的价值;但是同一位置可以容纳多个机器人停留,那么不妨再建一条容量为 ++\infty,费用为 00 的边。求最大费用最大流时,产生价值的边一定会满流,且流量只能为 11,另一条费用为 00 的边用来承载不采集标本的机器人。
最后求出的最大费用最大流即为答案。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/******************************************************************
Copyright: 11D_Beyonder All Rights Reserved
Author: 11D_Beyonder
Problem ID: 洛谷 P4012
Date: 8/5/2020
Description: Maximum-cost Flow
*******************************************************************/
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int inf=0x3f3f3f3f;
const int N=1003;
struct E
{
int to;
int cap;
int cost;
int Next;
};
E edge[N<<6];
int head[N],tot;
int incf[N],pre[N];
int dis[N];
bool inqueue[N];
int a,b;
int p,q;
int s,t;
void init();
int ID(int,int);
inline void add_edge(int,int,int,int);
bool SPFA();
int MCMF();
int main()
{
int i,j;
cin>>a>>b;
cin>>p>>q;
p++;
q++;
init();
//向东走的价值
for(j=1;j<=p;j++)//枚举纵坐标
{
for(i=1;i<=q-1;i++)//枚举横坐标
{
int x;
scanf("%d",&x);
add_edge(ID(i,j),ID(i+1,j),1,-x);
add_edge(ID(i,j),ID(i+1,j),inf,0);
}
}
//向北走的价值
for(i=1;i<=q;i++)//枚举横坐标
{
for(j=1;j<=p-1;j++)//枚举纵坐标
{
int x;
scanf("%d",&x);
add_edge(ID(i,j),ID(i,j+1),1,-x);
add_edge(ID(i,j),ID(i,j+1),inf,0);
}
}
//坐标要反着读
while(a--)
{
int k,x,y;
scanf("%d%d%d",&k,&y,&x);
x++;
y++;
add_edge(s,ID(x,y),k,0);
}
while(b--)
{
int r,x,y;
scanf("%d%d%d",&r,&y,&x);
x++;
y++;
add_edge(ID(x,y),t,r,0);
}
cout<<-MCMF()<<endl;
return 0;
}
void init()
{
tot=1;
memset(head,-1,sizeof(head));
s=0;
t=p*q+1;
}
inline void add_edge(int u,int v,int cap,int cost)
{
tot++;
edge[tot].to=v;
edge[tot].cap=cap;
edge[tot].cost=cost;
edge[tot].Next=head[u];
head[u]=tot;
tot++;
edge[tot].to=u;
edge[tot].cap=0;
edge[tot].cost=-cost;
edge[tot].Next=head[v];
head[v]=tot;
}
bool SPFA()
{
queue<int>q;
memset(dis,inf,sizeof(dis));
memset(inqueue,0,sizeof(inqueue));
q.push(s);
dis[s]=0;
inqueue[s]=1;
incf[s]=inf;
while(!q.empty())
{
int x=q.front();
q.pop();
inqueue[x]=0;
for(register int i=head[x];~i;i=edge[i].Next)
{
if(!edge[i].cap) continue;//剩余容量为0,不在残量网络中。
int y=edge[i].to;
if(dis[y]>dis[x]+edge[i].cost)
{
dis[y]=dis[x]+edge[i].cost;//松弛操作
incf[y]=min(incf[x],edge[i].cap);//最小剩余容量
pre[y]=i;//记录前驱
if(!inqueue[y])
{
inqueue[y]=1;
q.push(y);
}
}
}
}
if(dis[t]==inf) return 0;//汇点不可达,已经求出最大流
else return 1;
}
int MCMF()
{
int maxflow,mincost;
maxflow=mincost=0;
while(SPFA())
{
int x=t;
//沿着前驱倒着走增广路
while(x!=s)
{
int y=pre[x];
edge[y].cap-=incf[t];
edge[y^1].cap+=incf[t];
x=edge[y^1].to;
}
maxflow+=incf[t];
mincost+=dis[t]*incf[t];
}
return mincost;
}
int ID(int x,int y) {return (x-1)*p+y;}