bzoj 2039 人员雇佣

2个月没写网络流,已经忘得干干净净了。
经典最小割建模
bzoj 2039 人员雇佣_第1张图片
 x y 都选  B+D=ax+ay
不选x, 选y A+D+F=ay+3exy
选x,不选y B+C+E=ax+3exy
x,y都不选 A+C=2exy
这个东西我列错了3遍,两个选一个没有的是x对y,y对x的贡献和x对y的影响
都不选是exy*2
开始的时候忘要加双向边了。
然后我光荣的写了 put(x,y,f); put(y,x,-f); 竟然过了7个点!!!

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>

#define ll long long
#define inf 1e15
#define eps 1e-10
#define md
#define N 1010
using namespace std;
struct yts { int x,t,ne; ll f;} e[N*N*5];
int v[N],dep[N],q[N];
int num=1,S,T,n;
ll sum[N],E[N][N],a[N],ans=0;
void put(int x,int y,ll f)
{
num++; e[num].x=x; e[num].t=y; e[num].f=f;
e[num].ne=v[x]; v[x]=num;
}
void add(int x,int y,ll f)
{
put(x,y,f); put(y,x,0);
}
void build()
{
S=n+1; T=n+2;
for (int i=1;i<=n;i++) add(S,i,sum[i]);
for (int i=1;i<=n;i++)
for (int j=i+1;j<=n;j++)
{
if (E[i][j]!=0)
{
add(i,j,E[i][j]<<1);
add(j,i,E[j][i]<<1);
}
}
for (int i=1;i<=n;i++) add(i,T,a[i]);
}
bool bfs()
{
memset(dep,0,sizeof(dep));
int h=0,w=1,x,y;
q[1]=S; dep[S]=1;
while (h!=w)
{
x=q[++h];
for (int i=v[x];i;i=e[i].ne)
{
y=e[i].t;
if ((dep[y]==0)&&e[i].f>0)
{
dep[y]=dep[x]+1;
q[++w]=y;
}
}
}
return dep[T]>0;
}

ll dfs(int x,ll f)
{
if (x==T) return f;
ll used=0,now=0;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (e[i].f>0&&dep[y]==dep[x]+1)
{
now=dfs(y,min(f-used,e[i].f));
used+=now;
e[i].f-=now; e[i^1].f+=now;
//if (e[i].f) head[x]=i;
if (used==f) break;
}
}
if (used==0) dep[x]=-1;
//printf("x used %d %lld\n",x,used);
return used;
}

void dinic()
{
while (bfs())
{
//for (int i=1;i<=T;i++) head[i]=v[i];
ans-=dfs(S,inf);
//outit();
}
}

int main()
{
//freopen("employ8.in","r",stdin); freopen("employ8.ans","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
scanf("%lld",&E[i][j]);
sum[i]+=E[i][j]; ans+=E[i][j];
}
//printf("%lld\n",ans);
build();
//outit();
dinic();
printf("%lld\n",ans);
return 0;
}


你可能感兴趣的:(bzoj 2039 人员雇佣)