题目大意
有nnn个变量w[1],w[2],…,w[n]w[1],w[2],\dots,w[n]w[1],w[2],…,w[n],每个变量可以取−W-W−W或WWW。
有ppp个式子,形如
Hi=ai∣w[xi]−w[yi]∣+bi∣w[yi]−w[zi]∣+ci∣w[zi]−w[xi]∣+di(w[xi]−w[yi]),ei(w[yi]−w[zi])+fi(w[zi]−w[xi])H_i=a_i|w[x_i]-w[y_i]|+b_i|w[y_i]-w[z_i]|+c_i|w[z_i]-w[x_i]|+d_i(w[x_i]-w[y_i]),e_i(w[y_i]-w[z_i])+f_i(w[z_i]-w[x_i])Hi=ai∣w[xi]−w[yi]∣+bi∣w[yi]−w[zi]∣+ci∣w[zi]−w[xi]∣+di(w[xi]−w[yi]),ei(w[yi]−w[zi])+fi(w[zi]−w[xi])
有qqq个条件,每个条件表示为x,y,rx,y,rx,y,r。
- r=0r=0r=0表示w[x]≤w[y]w[x]\leq w[y]w[x]≤w[y]
- r=1r=1r=1表示w[x]=w[y]w[x]=w[y]w[x]=w[y]
- r=2r=2r=2表示w[x]<w[y]w[x]<w[y]w[x]<w[y]
求∑w[i]+∑Hi\sum w[i]+\sum H_i∑w[i]+∑Hi的最小值。保证存在方案。
1≤t≤101\leq t\leq 101≤t≤10,1≤n≤5001\leq n\leq 5001≤n≤500,1≤p,q≤10001\leq p,q\leq 10001≤p,q≤1000,1≤W≤1061\leq W\leq 10^61≤W≤106,0≤ai,bi,ci,di,ei,fi≤10000\leq a_i,b_i,c_i,d_i,e_i,f_i\leq 10000≤ai,bi,ci,di,ei,fi≤1000
题解
我们可以把题面看作变量选−1-1−1或111,最后再乘上WWW。
这题要用用最大流。对于每个变量,建一个点iii,源点sss向iii连边,iii向ttt连边。割这两条边分别表示取−1-1−1和111。
在ppp个式子中,我们把不带绝对值的拆开,分别加到各个点的系数viv_ivi上。对于带绝对值的,如ai∣w[xi]−w[yi]∣a_i|w[x_i]-w[y_i]|ai∣w[xi]−w[yi]∣,则在xix_ixi和yiy_iyi之间连一条流量为2ai2a_i2ai的无向边。
对于qqq个条件,w[x]<w[y]w[x]<w[y]w[x]<w[y]等价于w[x]=−1,w[y]=1w[x]=-1,w[y]=1w[x]=−1,w[y]=1;如果w[x]=w[y]w[x]=w[y]w[x]=w[y],就在w[x]w[x]w[x]和w[y]w[y]w[y]之间连一条流量为正无穷的边;如果w[x]≤w[y]w[x]\leq w[y]w[x]≤w[y],就从aaa向bbb连一条流量为正无穷的有向边。
因为存在负边权,所以我们需要把所有sss到iii和iii到ttt的边加上一个小于正无穷的值ptptpt,然后在最后减去n×ptn\times ptn×pt即可。
跑一遍最大流求最小割,然后减去n×ptn\times ptn×pt,再乘上WWW即可。
时间复杂度为O(t×maxflow(n,n+p+q))O(t\times maxflow(n,n+p+q))O(t×maxflow(n,n+p+q))。
code
#include<bits/stdc++.h>
using namespace std;
int T,n,ww,p,q,tot,s,t,cs[20005],l[20005],r[20005],d[20005],vd[20005];
long long ans,w[20005],v[20005];
long long inf=1e16,pt=1e10;
void add(int xx,int yy,long long zz){
l[++tot]=r[xx];cs[tot]=yy;r[xx]=tot;w[tot]=zz;
}
long long aug(int i,long long augco){
if(i==t) return augco;
long long augc=augco,dl=0;
int md=n-1;
for(int u=r[i];u;u=l[u]){
int j=cs[u];
if(w[u]>0){
if(d[i]==d[j]+1){
dl=min(augc,w[u]);
dl=aug(j,dl);
w[u]-=dl;
w[u^1]+=dl;
augc-=dl;
if(d[s]>=n) return augco-augc;
if(!augc) break;
}
if(md>d[j]) md=d[j];
}
}
if(augco==augc){
--vd[d[i]];
if(!vd[d[i]]) d[s]=n;
d[i]=md+1;
++vd[d[i]];
}
return augco-augc;
}
void sap(){
memset(d,0,sizeof(d));
vd[0]=n;
while(d[s]<n) ans+=aug(s,inf);
}
int main()
{
scanf("%d",&T);
while(T--){
scanf("%d%d%d%d",&n,&ww,&p,&q);
tot=1;
memset(r,0,sizeof(r));
for(int i=1;i<=n;i++) v[i]=1;
int x,y,z,a,b,c,d,e,f;
s=n+1;t=n+2;
for(int i=1;i<=p;i++){
scanf("%d%d%d%d%d%d%d%d%d",&x,&y,&z,&a,&b,&c,&d,&e,&f);
add(x,y,2*a);add(y,x,2*a);
add(y,z,2*b);add(z,y,2*b);
add(z,x,2*c);add(x,z,2*c);
v[x]+=d-f;
v[y]+=e-d;
v[z]+=f-e;
}
for(int i=1;i<=q;i++){
scanf("%d%d%d",&x,&y,&z);
if(z==0){
add(x,y,inf);add(y,x,0);
}
else if(z==1){
add(x,y,inf);add(y,x,inf);
}
else{
add(x,t,inf);add(t,x,0);
add(s,y,inf);add(y,s,0);
}
}
for(int i=1;i<=n;i++){
add(s,i,-v[i]+pt);add(i,s,0);
add(i,t,v[i]+pt);add(t,i,0);
}
ans=-n*pt;
n+=2;
sap();
printf("%lld\n",ans*ww);
}
return 0;
}
该文章介绍了一种利用最大流算法解决含有绝对值的线性表达式优化问题的方法。题目中涉及n个变量,每个变量可取-W到W的值,有p个式子和q个条件约束。通过建立网络流图并处理绝对值项,转换为求解最小割问题,最终计算总和的最小值。算法的时间复杂度与最大流算法相关。
867

被折叠的 条评论
为什么被折叠?



