网络流24题

1.餐巾计划问题

这道题目算这些题目里比较难的题目,详细的说一下

首先我们注意到每天要求的纸巾不同,那很显然最后流入汇点一定是分别流入的

考虑拆点

按照一般的思路我们从超级源向早上连边表示提供新的纸巾

接下来晚上流入汇点是否可以呢

我们考虑一下我们晚上是要有向某一天的早上(快洗慢洗)连边的操作的

这会导致进入汇点的流量不对

所以我们改变一下

早上向汇点连边,表示当天获得了xx条新纸巾

而源点向晚上连边,表示可以获得xx条旧纸巾

网上的题解写的是前一天晚上向下一天晚上连边,然后源点向每天早上连边

其实我们可以每天早上向下一天早上连边,然后从起点向1早上连INF,这样可以减少边数

另外晚上向慢洗快洗好的那天连边

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define rint register ll
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
#define me(x) memset(x,0,sizeof(x))
const ll N=5000;
const ll N2=N*20;
const ll INF=1e9;
bool inq[N];
ll head[N],d[N],p[N],aa[N],n,m,s,t,l,f[N];
struct re{
  ll a,b,c,flow,cost,from;
}a[N2];
void arr(ll x,ll y,ll z,ll cost)
{
  a[++l].a=head[x];
  a[l].b=y;
  a[l].c=z;
  a[l].cost=cost;
  a[l].from=x;
  head[x]=l;
}
ll flow,cost;
struct Dinic{
  ll n;
  bool bf()
  {
    rep(i,1,n) d[i]=INF;
    me(inq); p[s]=0; aa[s]=INF;
    queue<ll> q;
    q.push(s);
    while (!q.empty())
    {
      ll x=q.front(); q.pop(); inq[x]=0;
    for (rint u=head[x];u;u=a[u].a)
    {
      ll v=a[u].b;
      if (a[u].c>a[u].flow&&d[v]>d[x]+a[u].cost)
      {
        d[v]=d[x]+a[u].cost;
        p[v]=u;
        aa[v]=min(aa[x],a[u].c-a[u].flow);
        if (!inq[v])
        {
          q.push(v); inq[v]=1; 
        }
      }
    }
    }
    if (d[t]==INF) return(0);
    flow+=aa[t];
    cost+=d[t]*aa[t];
    ll x=t;
    while (x!=s)
    {
      ll u=p[x];
      a[u].flow+=aa[t];
      if (u%2) a[u+1].flow-=aa[t]; else a[u-1].flow-=aa[t];
      x=a[u].from;
    }
    return 1;
  }
  void mincost()
  {
    while (bf());
  }
}D;
ll pp,ff,nn,ss;
IL void arr2(ll x,ll y,ll z,ll cost)
{
  arr(x,y,z,cost);
  arr(y,x,0,-cost);
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);
  cin>>n;
  rep(i,1,n) cin>>f[i];
  s=0; t=2*n+1;
  cin>>pp>>m>>ff>>nn>>ss;
  arr2(s,1,INF,pp);
  rep(i,1,n)
  {
    arr2(i,t,f[i],0);
    if (i!=n) arr2(i,i+1,INF,0);
    arr2(s,i+n,f[i],0);
    if (i+nn<=n) arr2(i+n,i+nn,INF,ss);
    if (i+m<=n) arr2(i+n,i+m,INF,ff);
  }
  D.n=n*2+1;
  D.mincost();
  cout<<cost<<endl;
  return 0;
}

2. [CTSC1999]家园

分层图跑最大流,还是比较简单的

3. 飞行员配对方案问题

裸题

4.软件补丁问题

并不是网络流的题目

状压dp,由于转移存在环跑spfa

5.太空飞行计划问题

最小割裸题

6.试题库问题

裸题

7. 最小路径覆盖问题

二分图经典题目,前面的整理提到过了

将每个点拆成入点和出点,跑最大流

答案=点数-最大流

8.魔术球问题

这道题还是要写一下的

首先从小到大枚举a和第二题是一样的,这样子的效率是高于二分答案的

然后问题就变成了最小路径覆盖

当最小路径覆盖大于它的时候,答案就是a-1

然后输出路径的时候

我们可以直接利用当前图,因为第n+1个一定没有在前面的环中放上

然后我们查找到每个点的后一个点是什么

注意多个环的话终点是t,一个环的话没有被更新终点是0

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
#define me(x) memset(x,0,sizeof(x))
const int N=2e4;
const int N2=1e5;
const int INF=1e9;
int s,t,n,m,flow,l,nxt[N];
bool f[N];
struct re{
    int a,b,c,flow,x1,y1;
}a[N2];
struct Di{
  bool vis[N];
  int head[N],d[N];
  void arr(int x,int y,int z,int x1,int y1)
  {
    a[++l].a=head[x];
    a[l].b=y;
    a[l].c=z;
    a[l].x1=x1;
    a[l].y1=y1;
    head[x]=l;
  }
  bool bfs()
  {
    me(vis);
    queue<int> q;
    q.push(s); d[s]=0; vis[s]=1;
    while(!q.empty())
    {
      rint x=q.front(); q.pop();
      for (rint u=head[x];u;u=a[u].a)
      {
        rint v=a[u].b;
        if (!vis[v]&&a[u].c>a[u].flow)
        {
          vis[v]=1; q.push(v); 
          d[v]=d[x]+1;
        }
      }
    }
    return(vis[t]);
  }
  int dfs(int x,int y)
  {
    if (x==t||!y) return(y);
    int ans=0,f;
    for (rint u=head[x];u;u=a[u].a)
    {
      rint v=a[u].b;
      if (d[v]==d[x]+1&&(f=dfs(v,min(a[u].c-a[u].flow,y)))>0)
      {
        ans+=f;
        a[u].flow+=f;
        if (u%2) a[u+1].flow-=f; else a[u-1].flow-=f;
        y-=f;
        if (!y) break;
      }
    }
    return(ans);
  }
  void maxflow()
  {
    while(bfs()) 
      flow+=dfs(s,INF);
  }
}D;
#define arr2(x,y,z,a,b) D.arr(x,y,z,a,b),D.arr(y,x,0,b,a)
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);
  s=0; t=10000;
  cin>>n;
  rep(i,1,10000)
  {
    int x1=i*2-1,x2=i*2;
    arr2(s,x1,1,s,i); arr2(x2,t,1,i,t);
    rep(j,1,i-1)
    {
      int tmp=sqrt(i+j);
      if (tmp*tmp==i+j)
      { 
        arr2(j*2-1,x2,1,j,i);
      }
    }
    D.maxflow();
    if (i-flow>n)
    {
      rep(i,1,l)
      if (a[i].c==a[i].flow&&a[i].c) 
      {
        nxt[a[i].x1]=a[i].y1;
        int k1;
        k1++;
      }
      cout<<i-1<<endl;
      rep(j,1,i-1)
      {
        int tt=j;
        if (!f[j])
        {
          while (j!=t&&j)
          {
            f[j]=1; 
            cout<<j<<" ";
            j=nxt[j]; 
          }
          cout<<endl;
        }
        j=tt;
      }
      exit(0);
    }
  }
  return 0;
}

9.最长不下降子序列问题

10.航空路线问题

网络流的话是裸题,两个路径求最大经过点数,显然费用流

同时是dp经典题目

有向无环图两条并行路径dp

dp[i][j]转移的时候只能向max(i,j)转移

为什么呢 因为这样既可以保证每个可行状态都可到达

又能保证每个只会被算一次

#include <bits/stdc++.h>
using namespace std;
#define rint register int
#define IL inline
#define rep(i,h,t) for (rint i=h;i<=t;i++)
#define dep(i,t,h) for (rint i=t;i>=h;i--)
map<string,int> M1;
map<int,string> M2;
string s,s1,s2;
int n,m;
const int N=105;
int f[N][N],dp[N][N],jl1[N],jl2[N];
struct re{
  int a,b;
}pre[N][N];
IL void maxa(int x,int y,int k,int a1,int b1)
{
  if (dp[x][y]<k)
  {
    dp[x][y]=k; 
    pre[x][y].a=a1; pre[x][y].b=b1;
  }
}
int main()
{
  freopen("1.in","r",stdin);
  freopen("1.out","w",stdout);
  ios::sync_with_stdio(false);
  cin>>n>>m;
  rep(i,1,n)
  {
    cin>>s; 
    M1[s]=i; M2[i]=s;
  }
  rep(i,1,m)
  {
    cin>>s1>>s2;
    int x1=M1[s1],x2=M1[s2];
    if (x1>x2) swap(x1,x2);
    f[x1][x2]=1;
  }
  #define pd(x,y) ((x!=y)||(x==n))
  dp[1][1]=1;
  rep(i,1,n)
    rep(j,1,n)
      if (dp[i][j])
      {
        int tmp=dp[i][j]+1;
        rep(k,min(max(i,j)+1,n),n)
        {
          if (f[i][k]) maxa(k,j,tmp,i,j);
          if (f[j][k]) maxa(i,k,tmp,i,j);
        }
      }
  if (dp[n][n])
  {
  cout<<dp[n][n]-1<<endl;
  int now1=n,now2=n,cnt1=0,cnt2=0;
  while (!(now1==1&&now2==1))
  {
    if (pre[now1][now2].a!=now1)
    {
      jl1[++cnt1]=pre[now1][now2].a;
      now1=pre[now1][now2].a;
    } else
    {
      jl2[++cnt2]=pre[now1][now2].b;
      now2=pre[now1][now2].b;
    }
  }
  dep(i,cnt1,1) cout<<M2[jl1[i]]<<endl;
  cout<<M2[n]<<endl;
  rep(i,1,cnt2) cout<<M2[jl2[i]]<<endl;
} else cout<<"No Solution!"<<endl;
  return 0;
}

11.方格取数问题

裸题,可以发现网格图是个二分图,按照行号+列号的奇偶性来判断就行了

12.机器人路径规划问题(应该是题目有问题,如果起点是叶子节点就可以直接网络流了)

13.圆桌问题

14.骑士共存问题

15.火星探险问题

16.最长k可重线段集问题(ok 前面篇写过了)

17.最长k可重区间集问题(ok前面篇写过了

18.汽车加油行驶问题

19.孤岛营救问题

20.深海机器人问题

21.数字梯形问题

22.分配问题

23.运输问题

24.负载平衡问题

原文地址:https://www.cnblogs.com/yinwuxiao/p/9481476.html