bzoj 1927 [Sdoi2010]星际竞速——网络流

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

每个点拆点保证只经过一次。

主要是如果经过了这个点,这个点应该向汇点流过去表示经过了它。但这样就难以表示它接着往别的点走了。

发现是DAG。而且每个点都会要求经过。所以不妨认为连向它的点一定是已经经过了的点。

源点向每个点的入点连容量为1的边,表示走到这个点之后只能选择一个孩子走过去。从连向自己的点的入点向自己的出点连边,表示它能让自己“被经过”即流向汇点。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int N=1605,M=34805,INF=N;
int rdn()
{
  int ret=0;bool fx=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}
  while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();
  return fx?ret:-ret;
}
int Mn(int a,int b){return a<b?a:b;}
int n,m,t,hd[N],xnt=1,nxt[M],to[M],cap[M],w[M];
int dis[N],pre[N],info[N],ans; bool ins[N];
queue<int> q;
void add(int x,int y,int d)
{
  to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=d;
  to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-d;
}
bool spfa()
{
  memset(ins,0,sizeof ins);
  memset(dis,0x3f,sizeof dis);
  q.push(0);ins[0]=1;dis[0]=0;info[0]=INF;info[t]=0;
  while(q.size())
    {
      int k=q.front();q.pop();ins[k]=0;
      for(int i=hd[k],v;i;i=nxt[i])
    if(cap[i]&&dis[v=to[i]]>dis[k]+w[i])
      {
        dis[v]=dis[k]+w[i];
        pre[v]=i;info[v]=Mn(info[k],cap[i]);
        if(!ins[v])q.push(v),ins[v]=1;
      }
    }
  return info[t];
}
void ek()
{
  int s=info[t];
  for(int i=pre[t];i;i=pre[to[i^1]])
    {
      ans+=s*w[i];cap[i]-=s;cap[i^1]+=s;
    }
}
int main()
{
  n=rdn();m=rdn();t=(n<<1)+1;
  for(int i=1,j=n+1,d;i<=n;i++,j++)
    d=rdn(),add(0,i,0),add(0,j,d),add(j,t,0);
  for(int i=1,u,v,d;i<=m;i++)
    {
      u=rdn();v=rdn();d=rdn();
      if(u>v)swap(u,v); add(u,v+n,d);
    }
  while(spfa())ek();
  printf("%d\n",ans);
  return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/10155071.html
今日推荐