网络流24题 部分总结

网络流24题 部分总结

慢慢写吧。。。

以前做过一些了;

然后发现也做了不少了,集中写一下。

警告:

  1. 题目按照随机顺序排列。
  2. 文章中只有建模的方法。

最小路径覆盖问题

http://cogs.pro:8080/cogs/problem/problem.php?pid=728

题目即题解。。。

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define File
#define Fname "path3"
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
#define t (dis[i])
typedef long long ll;
il int gi(){
    rg int x=0;rg char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
const int maxn=151<<1,maxm=6002;
int id=0,fir[maxn],dis[maxm],nxt[maxm];
il vd add(int a,int b){
    nxt[++id]=fir[a],fir[a]=id,dis[id]=b;
}
bool vis[maxn];int match[maxn],in[maxn],to[maxn];
il bool dfs(int now){
    erep(i,now)
    if(!vis[t]){
        vis[t]=1;
        if(match[t]==-1||dfs(match[t])){match[t]=now;return 1;}
    }
    return 0;
}
int main(){
    freopen(Fname".in","r",stdin);
    freopen(Fname".out","w",stdout);
    rg int n=gi(),m=gi();
    while(m--){rg int i=gi(),j=gi();add(i,j+n);}
    memset(match,-1,sizeof match);
    int ans=0;
    drep(i,n,1){
    memset(vis,0,sizeof vis);
    vis[i]=1;if(dfs(i))++ans;
    }
    drep(i,n+n,n+1)if(match[i]+1)to[match[i]]=i-n,in[i-n]=1;
    rep(i,1,n)if(!in[i]){
    int now=i;printf("%d ",now);
    while(to[now])now=to[now],printf("%d ",now);
    puts("");
    }
    printf("%d\n",n-ans);
    return 0;
}

魔术球问题

http://cogs.pro:8080/cogs/problem/problem.php?pid=396

我会打表!

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int n,ans[]={233333,1,3,7,11,17,23,31,39,49,59,71,83,97,111,127,143,161,179,199,219,241,263,287,311,337,363,391,419,449,479,511,543,577,611,647,683,721,759,799,839,881,923,967,1011,1057,1103,1151,1199,1249,1299,1351,1403,1457,1511,1567,1623,1681,1739,1799,1859};
int main() {
freopen("balla.in","r",stdin);
freopen("balla.out","w",stdout);
    scanf("%d",&n);
    printf("%d",ans[n]);
    return 0;
}

正解是累加答案然后连边增广。不知道这是不是我当年写的了。丑的一批。唉。

#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<queue>
#include<cstring>
#define re register
#define inf 1<<29
#define il inline
#define rep(i,a,b) for(register int i=a;i<=b;++i)
#define file(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
const int N=1610;
struct Edge{
  int to,net,flow,cap;
  int fr;
}e[N*N];
int head[N*2],num_e,n,m;
il void add(int x,int y,int c){
  e[++num_e].to=y,e[num_e].cap=c,e[num_e].net=head[x],head[x]=num_e;
  e[num_e].fr=x;
}
const int oh=1604;
int s,t;
inline int gi() {
  re int res=0,f=1;re char ch=getchar();while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
  if(ch=='-')f=-1,ch=getchar();while(ch>='0'&&ch<='9')res=res*10+ch-'0',ch=getchar();return res*f;
}
int lev[N*2];
il bool bfs(){
  queue<int> q;
  memset(lev,0,sizeof(lev));
  q.push(s);lev[s]=1;re int u;
  while(!q.empty()){
    u=q.front();q.pop();
    for(int i=head[u];i!=-1;i=e[i].net){
      int to=e[i].to;
      if(!lev[to]&&e[i].cap>e[i].flow){
        lev[to]=lev[u]+1;
        q.push(to);
        if(lev[t]) return true;
      }
    }
  }
  return false;
}
int dfs(int x,int f){
  if(x==t) return f;
  int tag=0;
  for(int i=head[x];i!=-1;i=e[i].net){
    int to=e[i].to;
    if(lev[to]==lev[x]+1&&e[i].cap>e[i].flow){
      int c=dfs(to,min(e[i].cap-e[i].flow,f-tag));
      e[i].flow+=c;
      e[i^1].flow-=c;
      tag+=c;
      if(tag==f) break;
    }
  }
  if(!tag) lev[x]=0;
  return tag;
}
int Dinic(){
  re int flow=0;
  while(bfs())
    flow+=dfs(s,inf);
  return flow;
}
bool pd[N*2];
int nxt[N*2];
void get_ans(int n){
  for(re int i=0;i<=num_e;i+=2) {
    if(e[i].fr==s||e[i].to==t||e[i].cap!=e[i].flow) continue;
    nxt[e[i].fr]=e[i].to-oh;
    pd[e[i].to-oh]=1;
  }
  for(re int i=1;i<=n;i++)
    if(!pd[i]){
      re int u=i;
      printf("%d ",u);
      while(nxt[u]) u=nxt[u],printf("%d ",u);
      puts("");
    }
}
void init(){
  e[num_e-1].cap=0,e[num_e].cap=0;
  rep(i,0,num_e) e[i].flow=0;
  Dinic();
}
int main(){
  memset(head,-1,sizeof(head));num_e=-1;
  s=0,t=3210;
  re int i=0;
  m=gi();
  int f=0;
  for(;;){
    i++;
    rep(j,1,i-1) if((int)sqrt(i+j)*(int)sqrt(i+j)==i+j) add(j,i+oh,1),add(i+oh,j,0);
    add(s,i,1),add(i,s,0);
    add(i+oh,t,1),add(t,i+oh,0);
    f+=Dinic();
    if(i-f>m) break;
  }
  printf("%d\n",i-1);
  return 0;
}

搭配飞行员

http://cogs.pro:8080/cogs/problem/problem.php?pid=14

记得是我第一次写匈牙利把。

#include<cstdio>
#include<algorithm>
#include<cstring>
#define t (dis[i])
using namespace std;
const int maxn=110,maxm=510<<1;
void init();
void work();
bool dfs(int);
int main() {
    init(),work();
    return 0;
}
inline int gi();
int n,n1,m,fir[maxn],nxt[maxm],dis[maxm];
inline void adde(int a,int b,int id) {
    nxt[id]=fir[a],fir[a]=id,dis[id]=b;
}
bool vis[maxn];
int T[maxn];
void work() {
    memset(T,-1,sizeof(T));
    int ans=0;
    for(int i=1; i<=n1; i++) {
        memset(vis,0,sizeof(vis));
        ans+=dfs(i);
    }
    printf("%d\n",ans);
}
void init() {
    scanf("%d%d",&n,&n1);
    int a,b;
    while(scanf("%d%d",&a,&b)==2)adde(a,b,++m),adde(b,a,++m);
}
bool dfs(int now) {
    for(int i=fir[now]; i; i=nxt[i])
        if(!vis[t]) {
            vis[t]=1;
            if(T[t]==-1||dfs(T[t])){
                T[t]=now;return 1;
            }
        }
    return 0;
}

骑士共存

http://cogs.pro:8080/cogs/problem/problem.php?pid=746

你看他都帮你涂好颜色了

能互相攻击就连边,显然二分图,n*m-匹配=答案

#include<cstdio>
#include<algorithm>
#include<cstring>
#define check(x,y) (x>0&&x<=n&&y>0&&y<=n)
using namespace std;
const int X[]= {99999,-1,1,2,2};
const int Y[]= {888888888,2,2,1,-1};
int id[201][201],fir[40001],nxt[40001*8*2],dis[40001*8*2];
inline int gi() {
    int x=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0')f=(ch=='-')?-1:f,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*f;
}
int iiddd=0;
inline void adde(int a,int b) {
    nxt[++iiddd]=fir[a],fir[a]=iiddd,dis[iiddd]=b;
}
int N=0,n,m;
inline void init() {
    int a,b;
    n=gi(),m=gi();
    for(int i=1; i<=m; i++)a=gi(),b=gi(),id[a][b]=-1;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if(id[i][j]!=-1)id[i][j]=++N;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)if(id[i][j]>0)
            for(int k=1; k<5; k++)
                if(check((i+X[k]),(j+Y[k]))&&id[i+X[k]][j+Y[k]]>0) {
                    adde(id[i][j],id[i+X[k]][j+Y[k]]);
                    adde(id[i+X[k]][j+Y[k]],id[i][j]);
                }
}
int match[40001],vis[40001];
inline bool dfs(int now) {
    for(int i=fir[now]; i; i=nxt[i])
        if(!vis[dis[i]]) {
            int t=dis[i];
            vis[t]=1;
            if(match[t]==-1||dfs(match[t])) {
                match[t]=now;
                return 1;
            }
        }
    return 0;
}
inline void hun() {
    int ans=0;
    memset(match,-1,sizeof match);
    for(int i=1; i<=n; i++)
        for(int j=1; j<=n; j++)
            if((i+j)&1) {
                memset(vis,0,sizeof vis);
                ans+=dfs(id[i][j]);
            }
    printf("%d",N-ans);
}
int main() {
    init(),hun();
    return 0;
}

我以前代码写的好丑啊

最长递增子序列

http://cogs.pro:8080/cogs/problem/problem.php?pid=731

  1. 动态规划秒之。
  2. i向满足\(j>i且x_j>x_i\)的j连边;
    若\(f_i=s\),就i向T连边;
    S向每个点连边。
    权值均为1。
  3. 同2
    S向1连的边权值改为inf;
    若n向T有连边,权值改为inf。
  4. 有个坑点,这是最长不下降子序列。
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define t (dis[i])
using namespace std;
inline int gi() {
    int x=0,f=1;
    char ch=getchar();
    while(ch>'9'||ch<'0')f=(ch=='-')?-1:f,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*f;
}
inline void Dp();
inline void solve(int);
int X[501],F[501];
int n,K,S,T;
int main() {
    n=gi();
    for(int i=1; i<=n; i++)X[i]=gi();
    Dp();
    S=n+1,T=n+2,solve(1),solve(1000);
    return 0;
}
const int maxn=521,maxm=400001<<1;
int fir[maxn],dis[maxm],w[maxm],nxt[maxm],dep[maxn];
inline void Dp() {
    int ans=0;
    for(int i=1; i<=n; i++) {
        F[i]=1;
        for(int j=1; j<i; j++)if(X[j]<=X[i])F[i]=max(F[i],F[j]+1);
        ans=max(F[i],ans);
    }
    printf("%d\n",K=ans);
}
int Index=1;
inline void adde(int a,int b,int ww) {
    nxt[++Index]=fir[a],fir[a]=Index,dis[Index]=b,w[Index]=ww;
    if(ww)adde(b,a,0);
}
inline bool BFS() {
    queue<int>bfs;
    memset(dep,0,sizeof dep);
    bfs.push(S);
    bool yes[600]= {0};
    yes[S]=1,dep[S]=0;
    while(!bfs.empty()) {
        int now=bfs.front();
        for(int i=fir[now]; i; i=nxt[i])
            if(w[i]>0&&!yes[t])
                yes[t]=1,bfs.push(t),dep[t]=dep[now]+1;
        bfs.pop();
    }
    return yes[T];
}
inline int Dinic(int now,int h) {
    if(now==T)return h;
    int ans=0;
    for(int i=fir[now]; i; i=nxt[i])
        if(w[i]>0&&dep[t]==dep[now]+1) {
            int D=Dinic(t,min(h,w[i]));
            w[i]-=D,w[i^1]+=D,ans+=D,h-=D;
            if(h==0)return ans;
        }
    return ans;
}
inline void solve(int hehe) {
    memset(fir,0,sizeof fir);
    Index=1;
    for(int i=1; i<n; i++)
        for(int j=i+1; j<=n; j++)
            if(X[i]<=X[j]&&F[i]+1==F[j])adde(i,j,1);
    if(hehe^1) {
        adde(S,1,hehe);
        for(int i=2; i<=n; i++)if(F[i]==1)adde(S,i,1);
        for(int i=1; i<n; i++)if(F[i]==K)adde(i,T,1);
        if(F[n]==K)adde(n,T,hehe);
    } else {
        for(int i=1; i<=n; i++)if(F[i]==1)adde(S,i,1);
        for(int i=1; i<=n; i++)if(F[i]==K)adde(i,T,1);
    }
    int ans=0;
    while(BFS())ans+=Dinic(S,10000000);
    printf("%d\n",ans);
}

方格取数问题

懒得放链接了。。

结论题。。

先染色,S向黑点连边,权值为这个数;白点向T点连边,权值为这个数。相邻的点连边,权值为inf。

总数字-最大流=答案。。。

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
#define rep(a,b,c) for(rg int a=b;a<=c;a++)
#define drep(a,b,c) for(rg int a=b;a>=c;a--)
#define erep(a,b) for(rg int a=fir[b];a;a=nxt[a])
#define il inline
#define rg register
#define vd void
#define t (dis[i])
typedef long long ll;
il int gi(){
    rg int x=0;rg char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
const int maxn=30*30+10,maxm=30*30*4+100,S=1,T=2;
int fir[maxn],nxt[maxm],dis[maxm],w[maxm],id=1;
int X[]={23333,0,0,1,-1},Y[]={23333,1,-1,0,0};
il vd add(int a,int b,int c){
    nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=c;
    if(c)add(b,a,0);
}
int num[31][31];
bool vis[maxn];
il int ff(int now,int end,int minn){
    if(now==end)return minn;
    vis[now]=1;
    erep(i,now)
    if(!vis[dis[i]]&&w[i]){
        rg int down=ff(dis[i],end,min(minn,w[i]));
        if(down){w[i]-=down,w[i^1]+=down;return down;}
    }
    return 0;
}
int main(){
    int n=gi(),m=gi(),Id=2,sum=0,a=0;
    rep(i,1,n)rep(j,1,m){
    num[i][j]=++Id;
    sum+=a=gi();
    if((i+j)&1)add(S,num[i][j],a);
    else add(num[i][j],T,a);
    }
    rep(i,1,n)rep(j,1,m)if((i+j)&1)rep(k,1,4)
    if(num[i+X[k]][j+Y[k]])add(num[i][j],num[i+X[k]][j+Y[k]],666666666);
    int flow,Flow=0;
    while(flow=ff(S,T,666666666))Flow+=flow,memset(vis,0,sizeof vis);
    printf("%d\n",sum-Flow);
    return 0;
}

汽车加油行驶

最短路乱入。。

设f(x,y,k)为走到点(x,y),且还能走k条边的最小费用。

依题意连边就好了。。。

好像n<=100(把)

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<map>
#include<vector>
#include<queue>
using namespace std;
#define MAX 120
inline int read()
{
    int x=0,t=1;char ch=getchar();
    while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
    if(ch=='-')t=-1,ch=getchar();
    while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
    return x*t;
}
struct Line
{
    int v,next,w;
}e[MAX*MAX*MAX];
int h[MAX*MAX],cnt=1,tot;
int m[MAX*MAX],g[MAX][MAX],n,K,A,B,C;
bool vis[MAX*MAX][15];
inline void Add(int u,int v,int w)
{
    e[cnt]=(Line){v,h[u],w};h[u]=cnt++;
}
int dis[MAX*MAX][15];
void SPFA()
{
    memset(dis,63,sizeof(dis));
    dis[g[1][1]][K]=0;
    queue<int> Q,Q1;
    Q.push(g[1][1]);Q1.push(K);
    while(!Q.empty())
    {
        int u=Q.front(),t=Q1.front();
        Q.pop();Q1.pop();
        if(t!=0)
        {
            for(int i=h[u];i;i=e[i].next)
            {
                int v=e[i].v,gg=t-1,Dis=dis[u][t]+e[i].w;
                if(m[v])gg=K,Dis+=A;
                if(dis[v][gg]>Dis)
                {
                    dis[v][gg]=Dis;
                    if(!vis[v][gg])vis[v][gg]=true,Q.push(v),Q1.push(gg);
                }
            }
        }
        int v=u,gg=K,Dis=dis[u][t]+C+A;
        if(dis[v][gg]>Dis)
        {
            dis[v][gg]=Dis;
            if(!vis[v][gg])vis[v][gg]=true,Q.push(v),Q1.push(gg);
        }
        vis[u][t]=false;
    }
}
int main()
{
    n=read();K=read();A=read();B=read();C=read();
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
            g[i][j]=++tot,m[g[i][j]]=read();;
    for(int i=1;i<=n;++i)
        for(int j=1;j<=n;++j)
        {
            if(i!=n)Add(g[i][j],g[i+1][j],0);
            if(j!=n)Add(g[i][j],g[i][j+1],0);
            if(i!=1)Add(g[i][j],g[i-1][j],B);
            if(j!=1)Add(g[i][j],g[i][j-1],B);
        }
    SPFA();
    int ans=1e9;
    for(int i=0;i<=K;++i)ans=min(ans,dis[g[n][n]][i]);
    printf("%d\n",ans);
    return 0;
}

数字梯形

需要用到拆点的思想。。。

本来是每个点向下面两个点连边的,然而这样无法限制节点的流量,于是拆点

不想写了。。

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define inf 10000000
#define up +0
#define down +1
#define t (dis[i])
const int maxn=1001<<1,maxm=10000<<1,S=2000,T=2001;
using namespace std;
int fir[maxn],dis[maxm],w[maxm],cost[maxm],nxt[maxm],id=1,m,n;
int INDEX=0;
inline void add(int a,int b,int c,int d,int e=0){
  nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=c,cost[id]=d;
  if(c)add(b,a,0,e);
}
int num[21][41],Index[21][41];
inline void build(int a,int b){
  memset(fir,0,sizeof fir);
  for(int i=1;i<=m;i++)add(S,Index[1][i]up,1,0);
  for(int i=1;i<=m+n-1;i++)add(Index[n][i]down,T,a,0);
  for(int i=1;i<n;i++)
    for(int j=1;j<m+i;j++){
      add(Index[i][j]up,Index[i][j]down,a,-num[i][j],num[i][j]);
      add(Index[i][j]down,Index[i+1][j]up,b,0);
      add(Index[i][j]down,Index[i+1][j+1]up,b,0);
    }
  for(int j=1;j<m+n;j++)
      add(Index[n][j]up,Index[n][j]down,a,-num[n][j],num[n][j]);
}
int que[maxn],vis[maxn],pre[maxn],Dis[maxn];
inline bool spfa(){
  int hd=1,tl=1;
  for(int i=1;i<=INDEX;i++)Dis[i]=inf;
  memset(vis,0,sizeof vis);
  Dis[T]=inf,Dis[S]=0,que[tl++]=S,vis[S]=1;
  while(hd^tl){
    int now=que[hd];
    for(int i=fir[now];i;i=nxt[i])
      if(w[i]>0&&Dis[t]>Dis[now]+cost[i]){
    Dis[t]=Dis[now]+cost[i],pre[t]=i;
    if(!vis[t])vis[t]=1,que[tl]=t,tl=(tl+1)%maxn;
      }
    vis[now]=0,hd=(hd+1)%maxn;
  }
  return Dis[T]!=inf;
}
inline int end(int&flow,int sum=inf,int ret=0){
  for(int now=T,p;now^S;now=dis[p^1])p=pre[now],sum=min(sum,w[p]);
  for(int now=T,p;now^S;now=dis[p^1])p=pre[now],w[p]-=sum,w[p^1]+=sum,ret+=cost[p];
  flow+=sum;return ret*sum;
}
inline int maxflow(int ans=0,int flow=0){
  while(spfa())ans+=end(flow);
  return -ans;
}
int main(){
  scanf("%d%d",&m,&n);
  for(int i=0;i<n;i++)
    for(int j=1;j<=m+i;j++)
      scanf("%d",&num[i+1][j]),Index[i+1][j]=++INDEX,++INDEX;
  build(1,1),printf("%d\n",maxflow());
  build(inf,1),printf("%d\n",maxflow());
  build(inf,inf),printf("%d\n",maxflow());
  return 0;
}

分配问题

我真的懒得写了。。。

最长k可重区间集

离散化,值域[1,p]
之后从i向i+1(0<i<p)连边(容量为k),S向0连边(容量为k),p向T连边(容量为k)

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define il inline
#define rg register
#define vd void
#define sta static
typedef long long ll;
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=501,maxN=1100,S=0,T=maxN-1,maxm=((1001+501)<<1)+233;
int l[maxn],r[maxn],num[maxn<<1];
int fir[maxN],dis[maxm],nxt[maxm],w[maxm],cost[maxm],id=1;
il vd link(const int&x,const int&y,const int&_w,const int&_c){nxt[++id]=fir[x],fir[x]=id,dis[id]=y,w[id]=_w,cost[id]=_c;}
il vd Link(int x,int y,int _w,int _c){link(x,y,_w,_c),link(y,x,0,-_c);}
il bool SPFA(int&c){
    sta int que[maxN],hd,tl,dist[maxN],lst[maxN];
    sta bool inque[maxN]={0};
    memset(dist,63,sizeof dist);memset(lst,0,sizeof lst);
    hd=tl=0;que[tl++]=S;inque[S]=1;dist[S]=0;
    while(hd^tl){
        sta int x;x=que[hd];
        for(rg int i=fir[x];i;i=nxt[i])
            if(dist[x]+cost[i]<dist[dis[i]]&&w[i]){
                dist[dis[i]]=dist[x]+cost[i];lst[dis[i]]=i;
                if(!inque[dis[i]])inque[dis[i]]=1,que[tl++]=dis[i],tl%=maxN;
            }
        ++hd;hd%=maxN;inque[x]=0;
    }
    if(lst[T]==0)return 0;
    int flow=233333333;
    for(rg int i=lst[T];i;i=lst[dis[i^1]])flow=std::min(flow,w[i]);
    for(rg int i=lst[T];i;i=lst[dis[i^1]])c+=cost[i]*flow,w[i]-=flow,w[i^1]+=flow;
    return 1;
}
int main(){
    int n=gi(),k=gi(),p;
    for(rg int i=1;i<=n;++i){
        l[i]=gi(),r[i]=gi();
        num[++num[0]]=l[i],num[++num[0]]=r[i];
    }
    std::sort(num+1,num+2*n+1);
    p=std::unique(num+1,num+2*n+1)-num-1;
    for(rg int i=1;i<=n;++i)l[i]=std::lower_bound(num+1,num+p+1,l[i])-num,r[i]=std::lower_bound(num+1,num+p+1,r[i])-num;
    for(rg int i=0;i<p;++i)Link(i,i+1,k,0);
    for(rg int i=1;i<=n;++i)Link(l[i],r[i],1,-(num[r[i]]-num[l[i]]));
    Link(p,T,k,0);
    int c=0;while(SPFA(c));
    printf("%d\n",-c);
    return 0;
}

餐巾计划

费用流裸题(要烂尾了)

#include<cstdio>
#include<algorithm>
#define X(o) ((o)<<1)
#define Y(o) ((o)<<1|1)
#define t (dis[i])
#define rg register
using namespace std;
int n,r[222],P,fast,slow,fastc,slowc;
const int maxd=420,maxm=23333,S=410,T=411,inf=233333333;
inline int gi() {
    rg int x=0;
    rg char ch=getchar();
    while(ch>'9'||ch<'0')ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x;
}
int fir[maxd],nxt[maxm],dis[maxm],w[maxm],cost[maxm],id=1;
inline void add(int a,int b,int c,int d){
    nxt[++id]=fir[a],fir[a]=id,dis[id]=b,w[id]=c,cost[id]=d;
    if(c)add(b,a,0,-d);
}
inline void build(){
    for(rg int i=1;i<=n;i++){
        add(S,X(i),r[i],0),add(S,Y(i),inf,P);
        add(Y(i),T,r[i],0);
        add(X(i),X(i+1),inf,0);
        if(i+fast<=n)add(X(i),Y(i+fast),inf,fastc);
        if(i+slow<=n)add(X(i),Y(i+slow),inf,slowc);
    }
}
inline bool spfa(int&F,int&C){
    int que[maxd],pre[maxd],Dis[maxd];bool inque[maxd]={0};
    rg int hd=1,tl=1;
    que[tl++]=S,inque[S]=1;
    for(rg int i=1;i<=Y(n);i++)Dis[i]=inf;Dis[T]=inf,Dis[S]=0;
    while(hd^tl){
        rg int now=que[hd];
        for(rg int i=fir[now];i;i=nxt[i])
            if(w[i]>0&&Dis[t]>Dis[now]+cost[i]){
                Dis[t]=Dis[now]+cost[i],pre[t]=i;
                if(!inque[t])que[tl++]=t,tl%=maxd,inque[t]=1;
            }
        hd=(hd+1)%maxd,inque[now]=0;
    }
    if(Dis[T]==inf)return 0;
    rg int sum=inf;
    for(int now=T,p;now^S;now=dis[p^1])p=pre[now],sum=min(sum,w[p]);
    for(int now=T,p;now^S;now=dis[p^1])p=pre[now],w[p]-=sum,w[p^1]+=sum,C+=cost[p]*sum;
    F+=sum;
    return 1;
}
inline int mincost(int Flow=0,int Cost=0){
    while(spfa(Flow,Cost));return Cost;
}
int main(){
    n=gi();
    for(rg int i=1;i<=n;i++)r[i]=gi();
    P=gi(),fast=gi(),fastc=gi(),slow=gi(),slowc=gi();
    build();
    printf("%d\n",mincost());
    return 0;
}

最长k可重区间集

直接看我link部分吧。。

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cstring>
#define il inline
#define rg register
#define vd void
#define sta static
typedef long long ll;
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=501,maxN=1100,S=0,T=maxN-1,maxm=((1001+501)<<1)+233;
int l[maxn],r[maxn],num[maxn<<1];
int fir[maxN],dis[maxm],nxt[maxm],w[maxm],cost[maxm],id=1;
il vd link(const int&x,const int&y,const int&_w,const int&_c){nxt[++id]=fir[x],fir[x]=id,dis[id]=y,w[id]=_w,cost[id]=_c;}
il vd Link(int x,int y,int _w,int _c){link(x,y,_w,_c),link(y,x,0,-_c);}
il bool SPFA(int&c){
    sta int que[maxN],hd,tl,dist[maxN],lst[maxN];
    sta bool inque[maxN]={0};
    memset(dist,63,sizeof dist);memset(lst,0,sizeof lst);
    hd=tl=0;que[tl++]=S;inque[S]=1;dist[S]=0;
    while(hd^tl){
        sta int x;x=que[hd];
        for(rg int i=fir[x];i;i=nxt[i])
            if(dist[x]+cost[i]<dist[dis[i]]&&w[i]){
                dist[dis[i]]=dist[x]+cost[i];lst[dis[i]]=i;
                if(!inque[dis[i]])inque[dis[i]]=1,que[tl++]=dis[i],tl%=maxN;
            }
        ++hd;hd%=maxN;inque[x]=0;
    }
    if(lst[T]==0)return 0;
    int flow=233333333;
    for(rg int i=lst[T];i;i=lst[dis[i^1]])flow=std::min(flow,w[i]);
    for(rg int i=lst[T];i;i=lst[dis[i^1]])c+=cost[i]*flow,w[i]-=flow,w[i^1]+=flow;
    return 1;
}
int main(){
    int n=gi(),k=gi(),p;
    for(rg int i=1;i<=n;++i){
        l[i]=gi(),r[i]=gi();
        num[++num[0]]=l[i],num[++num[0]]=r[i];
    }
    std::sort(num+1,num+2*n+1);
    p=std::unique(num+1,num+2*n+1)-num-1;
    for(rg int i=1;i<=n;++i)l[i]=std::lower_bound(num+1,num+p+1,l[i])-num,r[i]=std::lower_bound(num+1,num+p+1,r[i])-num;
    for(rg int i=0;i<p;++i)Link(i,i+1,k,0);
    for(rg int i=1;i<=n;++i)Link(l[i],r[i],1,-(num[r[i]]-num[l[i]]));
    Link(p,T,k,0);
    int c=0;while(SPFA(c));
    printf("%d\n",-c);
    return 0;
}

最长k可重线段集

与上道题一毛一样,特判与y轴平行的线段。

// It is made by XZZ
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define il inline
#define rg register
#define vd void
#define sta static
#define int long long
il int gi(){
    rg int x=0,f=1;rg char ch=getchar();
    while(ch<'0'||ch>'9')f=ch=='-'?-1:f,ch=getchar();
    while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    return x*f;
}
const int maxn=501,maxN=1100,S=0,T=maxN-1,maxm=((1001+501)<<1)+233;
int l[maxn],r[maxn],len[maxn],num[maxn<<1];
int fir[maxN],dis[maxm],nxt[maxm],w[maxm],cost[maxm],id=1;
il vd link(const int&x,const int&y,const int&_w,const int&_c){nxt[++id]=fir[x],fir[x]=id,dis[id]=y,w[id]=_w,cost[id]=_c;}
il vd Link(int x,int y,int _w,int _c){link(x,y,_w,_c),link(y,x,0,-_c);}
il bool SPFA(int&c){
    sta int que[maxN],hd,tl,dist[maxN],lst[maxN];
    sta bool inque[maxN]={0};
    memset(dist,63,sizeof dist);memset(lst,0,sizeof lst);
    hd=tl=0;que[tl++]=S;inque[S]=1;dist[S]=0;
    while(hd^tl){
        sta int x;x=que[hd];
        for(rg int i=fir[x];i;i=nxt[i])
            if(dist[x]+cost[i]<dist[dis[i]]&&w[i]){
                dist[dis[i]]=dist[x]+cost[i];lst[dis[i]]=i;
                if(!inque[dis[i]])inque[dis[i]]=1,que[tl++]=dis[i],tl%=maxN;
            }
        ++hd;hd%=maxN;inque[x]=0;
    }
    if(lst[T]==0)return 0;
    int flow=233333333;
    for(rg int i=lst[T];i;i=lst[dis[i^1]])
        flow=std::min(flow,w[i]);
    for(rg int i=lst[T];i;i=lst[dis[i^1]])c+=cost[i]*flow,w[i]-=flow,w[i^1]+=flow;
    return 1;
}
main(){
    int n=gi(),k=gi(),p;
    for(rg int i=1;i<=n;++i){
        sta int x1,x2,y1,y2;
        x1=gi(),y1=gi(),x2=gi(),y2=gi();len[i]=(int)(sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1)));
        if(x1>x2)std::swap(x1,x2),std::swap(y1,y2);
        x1<<=1,x2<<=1;x1==x2?++x2:++x1;
        l[i]=x1,r[i]=x2;
        num[++num[0]]=l[i],num[++num[0]]=r[i];
    }
    std::sort(num+1,num+2*n+1);
    p=std::unique(num+1,num+2*n+1)-num-1;
    for(rg int i=1;i<=n;++i)l[i]=std::lower_bound(num+1,num+p+1,l[i])-num,r[i]=std::lower_bound(num+1,num+p+1,r[i])-num;
    for(rg int i=0;i<p;++i)Link(i,i+1,k,0);
    for(rg int i=1;i<=n;++i)Link(l[i],r[i],1,-len[i]);
    Link(p,T,k,0);
    int c=0;while(SPFA(c));
    printf("%lld\n",-c);
    return 0;
}

原文地址:https://www.cnblogs.com/xzz_233/p/8195110.html

时间: 2024-10-27 12:27:23

网络流24题 部分总结的相关文章

【网络流24题----14】孤岛营救问题

孤岛营救问题 Time Limit: 1 Sec  Memory Limit: 128 MB Description 1944年,特种兵麦克接到国防部的命令.要求马上赶赴太平洋上的一个孤岛,营救被敌军俘虏的大兵瑞恩.瑞恩被关押在一个迷宫里,迷宫地形复杂,但幸好麦克得到了迷宫的地形图.迷宫的外形是一个长方形,其南北方向被划分为 N行,东西方向被划分为 M列,于是整个迷宫被划分为 N×M个单元.每个单元的位置可用一个有序数对 (单元的行号,单元的列号)来表示.南北或东西方向相邻的 2个单元之间可能互

【网络流24题】

网络流 网络流24题 [最小路径覆盖问题] 关于输出路径,因为即使有反向弧经过左侧点也一定会改变左侧点的去向,若没连向右侧就会被更新到0,所以不用在意. mark记录有入度的右侧点,然后从没入度的右侧点开始把整条路径输出来即可. #include<cstdio> #include<algorithm> #include<cstring> using namespace std; const int maxn=100000,inf=0x3f3f3f3f; int n,m,

【网络流24题】魔术球问题

P1226 - [网络流24题]魔术球问题 Description 假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为 1,2,3,4......的球. (1)每次只能在某根柱子的最上面放球. (2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数. 试设计一个算法,计算出在n根柱子上最多能放多少个球.例如,在4 根柱子上最多可 放11个球. ′编程任务: 对于给定的n,计算在 n根柱子上最多能放多少个球. Input 第1 行有 1个正整数n,表示柱子数. Output 第一行是球

AC日记——[网络流24题]骑士共存 cogs 746

746. [网络流24题] 骑士共存 ★★☆   输入文件:knight.in   输出文件:knight.out   简单对比时间限制:1 s   内存限制:128 MB 骑士共存问题 «问题描述: 在一个n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示.棋盘 上某些方格设置了障碍,骑士不得进入. «编程任务: 对于给定的n*n个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑 士,使得它们彼此互不攻击. «数据输入: 由文件knight.in给出输入数据.第一行

网络流24题

刷刷基础题来巩固一下基础.. #1.飞行员配对方案问题 pdf链接 听说各大OJ的题面都和pdf不同.. 嗯连边匹配就行.. #include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <queue> using namespace std; const int Maxn = 110; struct node { int y, next

739. [网络流24题] 运输问题

739. [网络流24题] 运输问题 ★★   输入文件:tran.in   输出文件:tran.out   简单对比时间限制:1 s   内存限制:128 MB «问题描述:«编程任务:对于给定的m 个仓库和n 个零售商店间运送货物的费用,计算最优运输方案和最差运输方案.«数据输入:«结果输出:程序运行结束时,将计算出的最少运输费用和最多运输费用输出到文件tran.out中.输入文件示例 输出文件示例tran.in2 3220 280170 120 21077 39 105 150 186 1

【网络流24题】 No.12 软件补丁问题(最小转移代价 最短路)

[题意] T 公司发现其研制的一个软件中有 n 个错误, 随即为该软件发放了一批共 m 个补丁程序. 每一个补丁程序都有其特定的适用环境, 某个补丁只有在软件中包含某些错误而同时又不包含另一些错误时才可以使用.一个补丁在排除某些错误的同时, 往往会加入另一些错误.换句话说, 对于每一个补丁 i, 都有 2 个与之相应的错误集合 B1[i]和 B2[i],使得仅当软件包含 B1[i]中的所有错误, 而不包含 B2[i]中的任何错误时, 才可以使用补丁 i. 补丁 i 将修复软件中的某些错误 F1[

BZOJ_1221_ [HNOI2001]_软件开发(网络流24题,最小费用流)

描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1221 n天,每天需要r个毛巾,用完以后可以洗,要么花fa洗a天,要么花fb洗b天,毛巾不够了可以话f买一个,问最少需要多少钱. 分析 把每天拆成两个点:x[i]表示第i天的脏毛巾,y[i]表示第i天要用的毛巾. 1.s向x[i]连弧,容量为r[i],花费为0,表示每天会用脏r[i]条毛巾. 2.x[i]向x[i+1]连弧(注意边界),容量为INF,花费为0,表示把第i天的脏毛巾搁置到第i+1

【线性规划与网络流 24题】完成度(1/24)

PS:SDOI2016 Round1滚粗后蒟蒻开始做网络流来自我拯救(2016-04-11再过几天就要考先修课,现在做网络流24题貌似没什么用←退役节奏) 做的题目将附上日期,见证我龟速刷题. 1.飞行员配对方案问题 2016-04-11 二分图最大匹配问题,更新了一下$Dinic$模板,带上了当前弧优化和多路增广.这道题输出方案有很多种,可是没有special judge,所以没有A,但方案数是对的.合法的输出方案只能用匈牙利算法解决. #include<queue> #include<

题解:线性规划与网络流24题 T2 太空飞行计划问题

太空飞行计划问题 问题描述 W教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,-,Em},和进行这些实验需要使用的全部仪器的集合I={I1,I2,-In}.实验Ej 需要用到的仪器是I的子集Rj ∈ I.配置仪器Ik的费用为ck美元.实验Ej 的赞助商已同意为该实验结果支付pj 美元.W教授的任务是找出一个有效算法,确定在一次太空飞行中要进行哪些实验并因此而配置哪些仪器才能使太空飞行的净收益最大.这里净收