概念
费用流,全称最小费用最大流。是网络流中的一类题型。和最大流的区别就是每条边加了一个单位费用,每流
1
1
1的流量就要花费
m
o
n
e
y
e
money_e
moneye。要求的东西也变成了在最大流中费用最小的一个。
残留网络:在费用流中,残留网络还需要建立反向边的费用为
−
m
o
n
e
y
e
-money_e
−moneye,因为退货肯定要退钱。
算法
EK算法
方法
把求最大流
E
K
EK
EK算法中的
b
f
s
bfs
bfs换成最短路即可。因为残留网络中有负权边,所以
d
i
j
dij
dij会出问题,最好用
s
p
f
a
spfa
spfa。加上一个极大值可以让
d
i
j
dij
dij都变成正权,这个技巧有点像最大密度子图,详见我的博客最小割。但是一般
s
p
f
a
spfa
spfa足够了,所以这里只介绍
s
p
f
a
spfa
spfa的方法。不知道
E
K
EK
EK怎么做的可以看我的博客最大流。然后我们继续来看,我们可以求出一条增广路的费用和以及流量。因为这一条路径每一条边流量一定相等(少了流不过去,多了又不流量守恒),则设增广路中边集为
E
E
E,增广路路径之和为
M
o
n
e
y
Money
Money,所以路径的费用
=
∑
e
∈
E
(
m
o
n
e
y
e
×
f
l
o
w
)
=\displaystyle\sum_{e\in E}(money_e\times flow)
=e∈E∑(moneye×flow)
=
∑
e
∈
E
(
m
o
n
e
y
e
)
×
f
l
o
w
=\displaystyle\sum_{e\in E}(money_e)\times flow
=e∈E∑(moneye)×flow
=
M
o
n
e
y
×
f
l
o
w
=Money\times flow
=Money×flow那么,每次增广路的费用加上一个
M
o
n
e
y
×
f
l
o
w
Money\times flow
Money×flow即可。
时间复杂度
O
(
n
2
m
2
)
O(n^2m^2)
O(n2m2)。
例题
AcWing 2174
这个题套板子即可。
#include<bits/stdc++.h>
using namespace std;
const int NN=5004,MM=100004;
int head[NN],ne[MM],e[MM],w[MM],c[MM],f[NN],d[NN],pre[NN],idx=-1,flow,cost,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
void EK()
{
while(spfa())
{
flow+=f[t];
cost+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
}
int main()
{
memset(head,-1,sizeof(head));
int n,m;
scanf("%d%d%d%d",&n,&m,&s,&t);
for(int i=1;i<=m;i++)
{
int u,v,l,money;
scanf("%d%d%d%d",&u,&v,&l,&money);
add(u,v,l,money);
}
EK();
printf("%d %d",flow,cost);
return 0;
}
费用流解二分图最大权匹配
概念
二分图最大权匹配,就是二分图匹配加上了边权,要求一个边权最大的匹配。
方法
还是最大流解二分图的建图方式,两点之间的边加上费用即可。因为是求最大权,所以边权变成负的求最小费用最大流,最后再把 E K EK EK的结果变回正的即可。不知道怎么最大流解二分图的,见我的博客最大流。
例题
AcWing 2193
这个题目就是一个二分图最大权匹配问题。但是又要求一个最小权匹配,直接就正权求最小费用最大流即可。
#include<bits/stdc++.h>
using namespace std;
const int NN=104,MM=5204;
int head[NN],ne[MM],e[MM],w[MM],c[MM],f[NN],d[NN],pre[NN],idx=-1,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
memset(head,-1,sizeof(head));
int n;
scanf("%d",&n);
t=2*n+1;
for(int i=1;i<=n;i++)
{
add(s,i,1,0);
add(i+n,t,1,0);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;
scanf("%d",&x);
add(i,j+n,1,x);
}
printf("%d\n",EK());
for(int i=0;i<=idx;i+=2)
{
c[i]+=c[i^1];
c[i^1]=0;
w[i]=-w[i];
w[i^1]=-w[i^1];
}
printf("%d",-EK());
return 0;
}
技巧:拆点
概念
不知道怎么拆点的,见我的博客最大流。这里和最大流的拆点一样,只不过边加上了费用而已。在费用流中还有一种情况要拆点,就是有点权,拆点之间边的费用就是点权。
方法
和最大流的一样。
例题
AcWing 2191
这个题有点权,要拆点。第一问就是点只能用一次的情况,拆点限制为 1 1 1即可。第二问就是每个点用多次,把入点到出点的限制放开即可。第三问就是边的限制放开。
#include<bits/stdc++.h>
using namespace std;
const int NN=1004,MM=3124;
int g[20][40],id[20][40],head[NN],ne[MM],w[MM],c[MM],e[MM],d[NN],f[NN],pre[NN],idx,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
c[idx]=0;
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
int n,m,cnt=0;
scanf("%d%d",&m,&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=i+m-1;j++)
{
scanf("%d",&g[i][j]);
id[i][j]=++cnt;
}
t=2*cnt+2;
memset(head,-1,sizeof(head));
idx=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i+m-1;j++)
{
add(id[i][j]*2,id[i][j]*2+1,1,-g[i][j]);
if(i==1)
add(s,id[i][j]*2,1,0);
if(i==n)
add(id[i][j]*2+1,t,1,0);
if(i<n)
{
add(id[i][j]*2+1,id[i+1][j]*2,1,0);
add(id[i][j]*2+1,id[i+1][j+1]*2,1,0);
}
}
printf("%d\n",-EK());
memset(head,-1,sizeof(head));
idx=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i+m-1;j++)
{
add(id[i][j]*2,id[i][j]*2+1,1e9,-g[i][j]);
if(i==1)
add(s,id[i][j]*2,1,0);
if(i==n)
add(id[i][j]*2+1,t,1e9,0);
if(i<n)
{
add(id[i][j]*2+1,id[i+1][j]*2,1,0);
add(id[i][j]*2+1,id[i+1][j+1]*2,1,0);
}
}
printf("%d\n",-EK());
memset(head,-1,sizeof(head));
idx=-1;
for(int i=1;i<=n;i++)
for(int j=1;j<=i+m-1;j++)
{
add(id[i][j]*2,id[i][j]*2+1,1e9,-g[i][j]);
if(i==1)
add(s,id[i][j]*2,1,0);
if(i==n)
add(id[i][j]*2+1,t,1e9,0);
if(i<n)
{
add(id[i][j]*2+1,id[i+1][j]*2,1e9,0);
add(id[i][j]*2+1,id[i+1][j+1]*2,1e9,0);
}
}
printf("%d\n",-EK());
return 0;
}
AcWing 382
这个题有点权,需要拆点解决。把一个点拆成入点和出点,费用是价值的容量为 1 1 1,再来一种以后走的边,费用为 0 0 0容量为正无穷,求最大费用最大流。
#include<bits/stdc++.h>
using namespace std;
const int NN=10004,MM=30004;
int n,head[NN],ne[MM],w[MM],c[MM],e[MM],d[NN],f[NN],pre[NN],idx=-1,s,t=1;
bool st[NN];
int get(int x,int y)
{
return ((x-1)*n+y)*2;
}
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
int k;
scanf("%d%d",&n,&k);
memset(head,-1,sizeof(head));
add(s,get(1,1),k,0);
add(get(n,n)+1,t,k,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
{
int x;
scanf("%d",&x);
add(get(i,j),get(i,j)+1,1,-x);
add(get(i,j),get(i,j)+1,1e9,0);
if(i<n)
add(get(i,j)+1,get(i+1,j),1e9,0);
if(j<n)
add(get(i,j)+1,get(i,j+1),1e9,0);
}
printf("%d\n",-EK());
return 0;
}
AcWing 2184
这个题就是典型的多种状态。把每天的毛巾拆成两个点,一个是洗过,一个是没有洗。每天没洗过的毛巾可以流到下一天,也可以经过清洗流到干净的毛巾,快洗流到新毛巾的 i + t 1 i+t1 i+t1天,费用 m 1 m1 m1,慢洗同理。也可以购买新毛巾,费用是 m 0 m0 m0,新毛巾用了可以流到汇点。每天都会用完刚好 x x x条毛巾,所以会有 x x x条脏毛巾,但是他们流向汇点计算答案了,所以这里就改成从源点向脏毛巾的点流 x x x。
#include<bits/stdc++.h>
using namespace std;
const int NN=2004,MM=20004;
int head[NN],e[MM],ne[MM],c[MM],w[MM],idx=-1,f[NN],d[NN],pre[NN],s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
d[v]=d[u]+w[i];
f[v]=min(f[u],c[i]);
pre[v]=i;
if(!st[v])
{
st[v]=true;
q.push(v);
}
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
int n,m0,t1,m1,t2,m2;
scanf("%d%d%d%d%d%d",&n,&m0,&t1,&m1,&t2,&m2);
t=2*n+1;
memset(head,-1,sizeof(head));
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
add(s,i,x,0);
add(i+n,t,x,0);
add(s,i+n,1e9,m0);
if(i<n)
add(i,i+1,1e9,0);
if(i+t1<=n)
add(i,n+i+t1,1e9,m1);
if(i+t2<=n)
add(i,n+i+t2,1e9,m2);
}
printf("%d",EK());
return 0;
}
上下界可行流
概念
不知道上下界可行流见我的博客最大流,这里就是多了个费用。但是在费用流中要求从源点和流向汇点的边是没有费用的,因为它们只是为了流量守恒。
方法
和最大流的上下界可行流一样。
例题
AcWing 969
这个题就是一个上下界可行流。但是这个是没有上界的,所以上界可以设为正无穷。于是,上界减下界还是正无穷。前一天的志愿者可以到后一天,到了最后一天就流出去。然后我们发现,原图 G G G从志愿者结束工作的时间是流到 t t t了一些人,开始从 s s s流进来了一些开始工作的志愿者,但是新图 G G G的源点和汇点是满足流量守恒的,不能提供志愿者。所以为了流量守恒,结束向开始连一条边即可。如果要公式化的解释我看到了网上一篇解释地特别漂亮的博客。围观点我
#include<bits/stdc++.h>
using namespace std;
const int NN=1004,MM=24004;
int head[NN],ne[MM],w[MM],c[MM],e[MM],d[NN],f[NN],pre[NN],idx=-1,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
memset(head,-1,sizeof(head));
t=n+2;
int last=0;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(last>x)
add(s,i,last-x,0);
else
add(i,t,x-last,0);
add(i,i+1,1e9,0);
last=x;
}
add(s,n+1,last,0);
for(int i=1;i<=m;i++)
{
int b,e,money;
scanf("%d%d%d",&b,&e,&money);
add(e+1,b,1e9,money);
}
printf("%d\n",EK());
return 0;
}
费用流应用
AcWing 2192
每个仓库有货物,从源点连容量为货物数量,费用为 0 0 0的边。每个店铺要货物,从店铺向汇点连容量为店铺需求货物的数量,费用为 0 0 0的边。因为保证货物供需平衡,所以不用判断是否满流。再从仓库到店铺连流量为正无穷,费用为运输费用的边即可。
#include<bits/stdc++.h>
using namespace std;
const int NN=154,MM=10004;
int head[NN],ne[MM],e[MM],w[MM],c[MM],f[NN],d[NN],pre[NN],idx=-1,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
memset(head,-1,sizeof(head));
int n,m;
scanf("%d%d",&n,&m);
t=n+m+1;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
add(s,i,x,0);
}
for(int i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
add(i+n,t,x,0);
}
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add(i,j+n,1e9,x);
}
printf("%d\n",EK());
for(int i=0;i<=idx;i+=2)
{
c[i]+=c[i^1];
c[i^1]=0;
w[i]=-w[i];
w[i^1]=-w[i^1];
}
printf("%d",-EK());
return 0;
}
AcWing 2194
可以先人为地给每个仓库减去平均值,然后多出来的由源点给,少的最后要流到汇点。因为保证有解,所以不用判断是否满流。最后给相邻两个仓库连上费用为 1 1 1容量正无穷的边即可。
#include<bits/stdc++.h>
using namespace std;
const int NN=104,MM=604;
int head[NN],ne[MM],e[MM],w[MM],c[MM],f[NN],d[NN],pre[NN],a[NN],idx=-1,s,t;
bool st[NN];
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
memset(head,-1,sizeof(head));
int n,sum=0;
scanf("%d",&n);
t=n+1;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];
}
sum/=n;
for(int i=1;i<=n;i++)
{
a[i]-=sum;
if(a[i]>0)
add(s,i,a[i],0);
else
add(i,t,-a[i],0);
add(i,(i-2+n)%n+1,1e9,1);
add(i,i%n+1,1e9,1);
}
printf("%d",EK());
return 0;
}
AcWing 2195
本题和AcWing 382非常像,因为本题是边权,所以不用拆点。
#include<bits/stdc++.h>
using namespace std;
const int NN=504,MM=5004;
int head[NN],ne[MM],w[MM],c[MM],e[MM],d[NN],f[NN],pre[NN],idx=-1,s,t,n,m;
bool st[NN];
int sum(int x,int y)
{
return (x-1)*(m+1)+y;
}
void add(int u,int v,int l,int money)
{
e[++idx]=v;
ne[idx]=head[u];
c[idx]=l;
w[idx]=money;
head[u]=idx;
e[++idx]=u;
ne[idx]=head[v];
w[idx]=-money;
head[v]=idx;
}
bool spfa()
{
memset(f,0,sizeof(f));
memset(d,0x3f,sizeof(d));
queue<int>q;
q.push(s);
f[s]=1e9;
d[s]=0;
st[s]=true;
while(q.size())
{
int u=q.front();
q.pop();
st[u]=false;
for(int i=head[u];~i;i=ne[i])
{
int v=e[i];
if(c[i]&&d[v]>d[u]+w[i])
{
f[v]=min(f[u],c[i]);
d[v]=d[u]+w[i];
pre[v]=i;
if(st[v])
continue;
q.push(v);
st[v]=true;
}
}
}
return f[t]>0;
}
int EK()
{
int res=0;
while(spfa())
{
res+=d[t]*f[t];
for(int i=t;i!=s;i=e[pre[i]^1])
{
c[pre[i]]-=f[t];
c[pre[i]^1]+=f[t];
}
}
return res;
}
int main()
{
int ss,ts;
scanf("%d%d%d%d",&ss,&ts,&n,&m);
memset(head,-1,sizeof(head));
t=NN-1;
for(int i=1;i<=n+1;i++)
for(int j=1;j<=m;j++)
{
int x;
scanf("%d",&x);
add(sum(i,j),sum(i,j+1),1,-x);
add(sum(i,j),sum(i,j+1),1e9,0);
}
for(int i=1;i<=m+1;i++)
for(int j=1;j<=n;j++)
{
int x;
scanf("%d",&x);
add(sum(j,i),sum(j+1,i),1,-x);
add(sum(j,i),sum(j+1,i),1e9,0);
}
for(int i=1;i<=ss;i++)
{
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
add(s,sum(x+1,y+1),k,0);
}
for(int i=1;i<=ts;i++)
{
int k,x,y;
scanf("%d%d%d",&k,&x,&y);
add(sum(x+1,y+1),t,k,0);
}
printf("%d\n",-EK());
return 0;
}