这题的题面描述。。有点问题。。坐标写的很乱。
这道题其实和著名DP问题方格取数很像qwq
我们发现机器人可以重复经过边,但只能对答案贡献一次,所以两点间连两条边。一条是容量1带的费用。
另一条没费用容量INF。用拆点吗?不用的。因为这个权值是走过边会有,所以这题中不用拆点。
起点连S容量机器人,终点连T容量机器人。注意算好点的坐标别发生计算错误调半天这样的傻事qwq。
#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;
const int INF=1e9+7;
struct edge{
int u,to,next,w,c;
}e[MAXN<<1];
int head[MAXN],cnt=1;
inline void add(int u,int v,int w,int cost){
e[++cnt]=(edge){u,v,head[u],w,cost},head[u]=cnt;
e[++cnt]=(edge){v,u,head[v],0,-cost},head[v]=cnt;
}
int p,q,a,b,s,t;
bool vis[MAXN];
int dis[MAXN],pre[MAXN];
queue<int>qq;
bool SPFA(int x){
memset(vis,0,sizeof(vis));
memset(pre,0,sizeof(pre));
for(int i=s;i<=t;i++)dis[i]=-INF;
qq.push(x);vis[x]=1;dis[x]=0;
while(qq.size()){
int u=qq.front();qq.pop();
vis[u]=0;
for(int i=head[u];i;i=e[i].next){
int v=e[i].to,w=e[i].c;
if(e[i].w){
if(dis[u]+w>dis[v]){
dis[v]=dis[u]+w;pre[v]=i;
if(!vis[v]){
qq.push(v);
vis[v]=1;
}
}
}
}
}
if(dis[t]==-INF)return 0;
return 1;
}
int MC(){
int ans=0;
while(SPFA(s)){
int tem=INF;
for(int i=pre[t];i;i=pre[e[i].u]){
tem=min(tem,e[i].w);
}
for(int i=pre[t];i;i=pre[e[i].u]){
e[i].w-=tem;e[i^1].w+=tem;
ans+=tem*e[i].c;
}
}
return ans;
}
int main(){
int tem;
scanf("%d%d%d%d",&a,&b,&p,&q);
p++;q++;
s=0,t=p*q+1;//(x,y)按照 y进制 一个y是一行x
for(int i=0;i<p;i++){//坐标为(j,i)
for(int j=0;j<q-1;j++){
int now=i*q+j+1;
scanf("%d",&tem);
add(now,now+1,1,tem);
add(now,now+1,INF,0);
}
}
for(int i=0;i<q;i++){//坐标为(i,j)
for(int j=0;j<p-1;j++){
int now=j*q+i+1;
scanf("%d",&tem);
add(now,now+q,1,tem);
add(now,now+q,INF,0);
}
}
for(int i=1;i<=a;i++){
int k,x,y;
scanf("%d%d%d",&k,&y,&x);
int now=y*q+x+1;
// cout<<now<<"ok"<<endl;
add(s,now,k,0);
}
for(int i=1;i<=b;i++){//横行q个点,纵列p个点
int k,x,y;
scanf("%d%d%d",&k,&y,&x);
int now=y*q+x+1;
// cout<<"p:"<<p<<endl;
// cout<<"q:"<<q<<endl;
// cout<<now<<"ok"<<endl;
add(now,t,k,0);
}
printf("%d\n",MC());
return 0;
}