变量(variable)
【题目描述】
有n个变量w[1]~w[n],每个变量可以取W或-W。
有p个式子,形如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])。
有q个条件,形如w[x]<=w[y]或w[x]=w[y]或w[x]<w[y]。
最小化sigma(wi)+sigma(Hi)。
【输入数据】
第一行一个整数t表示数据组数。
每组数据第一行四个整数n,W,p,q表示节点数。
接下来p行每行九个整数xi,yi,zi,ai,bi,ci,di,ei,fi。
接下来q行每行三个整数x,y,r。
r=0表示w[x]<=w[y];r=1表示w[x]=w[y];r=2表示w[x]<w[y]。
保证存在方案。
【输出数据】
每组数据输出一行一个整数表示sigma(wi)+sigma(Hi)的最小值。
【样例输入】
1
3 1 1 1
1 2 3 1 1 1 1 1 1
1 2 2
【样例输出】
3
【数据范围】
对于30%的数据,n<=15,p,q<=20。
对于100%的数据,t<=10,n<=500,p,q<=1000,1<=W<=10^6,
0<=ai,bi,ci,di,ei,fi<=1000。
神奇的最小割
利用每个点只有两个取值拆点,选哪个取值就割那一边,然后将绝对值转化为共同选择两点(每个点代表一个点的一个取值)的代价,只要不输出方案就美滋滋,负边加上一个大整数,要小于inf,时刻将意义记住,关于二元关系,因为取值只有两个,直接特殊判断连边,容量inf,表示不能割,一个点不能有两个值,于是在i和i+n之间连inf,表示不能割
ACcode:
/*
神奇的最小割
源点S,汇点T
S->x X取+W
x+n->T X取-W
x->y+n 表示x连S,y连T,所以他们之间绝对值的差为2*W
x<=y 所以x->y+n不能割,权值为inf
x<y 所以x=w,y=-w
x==y 所以x->y+n,y->x+n不能割
w和-w 不能同时选,所以x->-x不能割
防止负边,可能负的2n条边+上极大值(小于inf??)
*/
#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#define maxn 1005
#define maxm 1000005
#define LL long long
using namespace std;
int n,p,q;
const LL inf=2147483647000000;
const LL Bignum=100000000000;
LL w,v[2][505],dis[505][505];
int Prev[maxm],to[maxm],info[maxn],cnt_e,S=0,T;
LL cap[maxm];
void Node(int u,int v,LL c){
cnt_e++;
Prev[cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v;
cap[cnt_e]=c;
}
void Line(int u,int v,LL c){
Node(u,v,c);
Node(v,u,0);
}
int vd[maxn],d[maxn],flag;
LL stm;
LL aug(int now,LL Max){
if(now==T)
return Max;
LL st=Max,inc;
int Min=T;
for(int i=info[now];i;i=Prev[i])
if(cap[i]){
if(d[to[i]]+1==d[now]){
inc=aug(to[i],min(st,cap[i]));
cap[i]-=inc;
cap[i^1]+=inc;
st-=inc;
if(!st || flag) return Max-st;
}
Min=min(Min,d[to[i]]);
}
if(Max==st){
!(--vd[d[now]]) && (flag=1);
vd[d[now]=Min+1]++;
}
return Max-st;
}
LL sap(){
memset(d,0,sizeof d);
memset(vd,0,sizeof vd);
vd[0]=T+1;
stm=flag=0;
while(!flag)
stm+=aug(S,inf);
return stm;
}
int main(){
freopen("variable.in","r",stdin);
freopen("variable.out","w",stdout);
int cas;
scanf("%d",&cas);
while(cas--){
memset(v,0,sizeof v);
memset(dis,0,sizeof dis);
memset(info,0,sizeof info);
cnt_e=1;
scanf("%d%lld%d%d",&n,&w,&p,&q);
LL x,y,z,a,b,c,d,e,f;
for(int i=1;i<=p;i++){
scanf("%lld%lld%lld%lld%lld%lld%lld%lld%lld",&x,&y,&z,&a,&b,&c,&d,&e,&f);
v[0][x]+=(d-f)*w;v[1][x]-=(d-f)*w;
v[0][y]+=(e-d)*w;v[1][y]-=(e-d)*w;
v[0][z]+=(f-e)*w;v[1][z]-=(f-e)*w;
dis[x][y]+=2*a*w;
dis[y][x]+=2*a*w;
dis[y][z]+=2*b*w;
dis[z][y]+=2*b*w;
dis[z][x]+=2*c*w;
dis[x][z]+=2*c*w;
}
for(int i=1;i<=q;i++){
scanf("%lld%lld%lld",&x,&y,&z);
if(z==0)
dis[y][x]+=inf;
if(z==1)
dis[x][y]+=inf,dis[y][x]+=inf;
if(z==2)
v[0][x]+=inf,v[1][y]+=inf;
}
S=2*n+1,T=2*n+2;
for(int i=1;i<=n;i++){
Line(S,i,v[0][i]+w+Bignum);
Line(i+n,T,v[1][i]-w+Bignum);
Line(i,i+n,inf);
for(int j=1;j<=n;j++)
if(dis[i][j])
Line(i,j+n,dis[i][j]);
}
printf("%lld\n",sap()-Bignum*n);
}
}