bzoj 1927 星际竞速 —— 最小费用最大流

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1927

首先注意到这是个DAG;

考虑每个点从哪里来,可以是瞬移来的,也可以是从某个点走过来的,而从每个点走出去只能用一次;

所以拆点,i 表示从这个点走出去,n+i 表示来到这个点;

建图:

1.瞬移:S 向 n+i 连边权 a[i],流量1的边

2.走过来:如果 i 能走到 j,那么 i 向 n+j 连边权 w,流量1的边

然后 S 向 i 连边权0,流量1的边,表示一个点只能走出去一次;

n+i 向 T 连边权0,流量1的边,表示一个点只能到达一次;

因为是DAG,所以不会有环,而且一定会有一个点瞬移开始;

建图真妙啊!

代码如下:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
int const xn=1605,xm=4e4+5,inf=0x3f3f3f3f;
int n,m,S,T,hd[xn],ct=1,to[xm],nxt[xm],w[xm],c[xm],dis[xn],inc[xn],pre[xn];
bool vis[xn];
queue<int>q;
int rd()
{
  int ret=0,f=1; char ch=getchar();
  while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return f?ret:-ret;
}
void ade(int x,int y,int z,int f){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct; w[ct]=z; c[ct]=f;}
void add(int x,int y,int z,int f){ade(x,y,z,f); ade(y,x,-z,0);}
bool bfs()
{
  memset(dis,0x3f,sizeof dis);
  dis[S]=0; q.push(S); vis[S]=1; inc[S]=inf;//inc
  while(q.size())
    {
      int x=q.front(); q.pop(); vis[x]=0;
      for(int i=hd[x],u;i;i=nxt[i])
    if(dis[u=to[i]]>dis[x]+w[i]&&c[i])
      {
        dis[u]=dis[x]+w[i]; pre[u]=i;
        inc[u]=min(inc[x],c[i]);
        if(!vis[u])vis[u]=1,q.push(u);
      }
    }
  return dis[T]!=inf;
}
void upd()
{
  int x=T;
  while(x!=S)
    {
      int i=pre[x];
      c[i]-=inc[T]; c[i^1]+=inc[T];
      x=to[i^1];
    }
}
int main()
{
  n=rd(); m=rd(); S=0; T=(n<<1)+1;
  for(int i=1,x;i<=n;i++)
    x=rd(),add(S,i,0,1),add(S,n+i,x,1),add(n+i,T,0,1);
  for(int i=1,x,y,z;i<=m;i++)
    {
      x=rd(); y=rd(); z=rd();
      if(x>y)swap(x,y);
      add(x,n+y,z,1);//n+y
    }
  int ans=0;
  while(bfs())ans+=dis[T]*inc[T],upd();
  printf("%d\n",ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Zinn/p/10134831.html
今日推荐