8/26 网络流Dinic算法+最小割+cf

Dinic算法相较于EK()算法效率更高

P3254 圆桌问题

思路:可用二分图来写,本题在于练习Dinic算法。
1.建立一个超级源点,连接每个代表团,权值为每个代表团的人数;
2.建立一个超级汇点,将每个圆桌连接汇点,权值为圆桌可容纳的人数;
3.将每个代表团连接每个圆桌,由于每个代表团不会安排两个人去同一个圆桌,因此权值都为1.

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
struct edge
{
    int to,c,nxt;
}e[N];
int n,m,r[N],c[N],s,t,sum;
int head[N],idx=1;
int d[N],cur[N];
void add(int a,int b,int c)
{
    e[++idx]={b,c,head[a]};
    head[a]=idx;
}
bool bfs()
{
    memset(d,0,sizeof d);
    queue<int>q;
    q.push(s);
    d[s]=1;
    while(q.size())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(d[v]==0&&e[i].c)
            {
                d[v]=d[u]+1;
                q.push(v);
                if(v==t) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int mf)
{
    if(u==t) return mf;
    int sum=0;
    for(int i=cur[u];i;i=e[i].nxt)
    {
        cur[u]=i;
        int v=e[i].to;
        if(d[v]==d[u]+1&&e[i].c)
        {
            int f=dfs(v,min(mf,e[i].c));
            e[i].c-=f;
            e[i^1].c+=f;
            sum+=f;
            mf-=f;
            if(mf==0) break;
        }
    }
    if(sum==0) d[u]=0;
    return sum;
}
int dinic()
{
    int flow=0;
    while(bfs())
    {
        memcpy(cur,head,sizeof head);
        flow+=dfs(s,inf);
    }
    return flow;
}
void solve()
{
    cin>>m>>n;
    s=0,t=n+m+1;
    for(int i=1;i<=m;i++)
    {
        cin>>r[i];sum+=r[i];
        add(0,i,r[i]);  //建立一个超级源点
        add(i,0,0);
    }
    for(int i=1;i<=n;i++)
    {
        cin>>c[i];
        add(i+m,t,c[i]);//建立一个超级汇点
        add(t,i+m,0);
    }
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=n;j++)
        {
            add(i,j+m,1);
            add(j+m,i,0);
        }
    }
    int ans=dinic();
    if(ans!=sum)
        cout<<0<<endl;
    else
    {
        cout<<1<<endl;
        for(int k=1;k<=m;k++)
        {
            for(int i=head[k];i;i=e[i].nxt)
            {
                if(e[i].to>m&&e[i].to<=m+n&&!e[i].c)
                    cout<<e[i].to-m<<" ";
            }
            cout<<endl;
        }
    }
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}


最小割

大佬讲解:https://www.bilibili.com/video/BV1iG411s7iX?spm_id_from=333.999.0.0&vd_source=91973ada1213cf6ba2cbb43a2bebd2e8
最小割最大流定理:最小割的容量等于最大流的容量

P1344 [USACO4.4]追查坏牛奶Pollutant Control

三个问题:
最小割、最小割的划分、最小割的最少边数
代码:

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=7e5+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
struct edge
{
    int to,c,nxt;
}e[N];
int n,m,a[N],b[N],s,t;
int head[N],idx=1;
int d[N],cur[N];
void add(int a,int b,int c)
{
    e[++idx]={b,c,head[a]};
    head[a]=idx;
}
bool bfs()
{
    memset(d,0,sizeof d);
    queue<int>q;
    q.push(s);
    d[s]=1;
    while(q.size())
    {
        int u=q.front();q.pop();
        for(int i=head[u];i;i=e[i].nxt)
        {
            int v=e[i].to;
            if(d[v]==0&&e[i].c)
            {
                d[v]=d[u]+1;
                q.push(v);
                if(v==t) return 1;
            }
        }
    }
    return 0;
}
int dfs(int u,int mf)
{
    if(u==t) return mf;
    int sum=0;
    for(int i=cur[u];i;i=e[i].nxt)
    {
        cur[u]=i;
        int v=e[i].to;
        if(d[v]==d[u]+1&&e[i].c)
        {
            int f=dfs(v,min(mf,e[i].c));
            e[i].c-=f;
            e[i^1].c+=f;
            sum+=f;
            mf-=f;
            if(mf==0) break;
        }
    }
    if(sum==0) d[u]=0;
    return sum;
}
int dinic()
{
    int flow=0;
    while(bfs())
    {
        memcpy(cur,head,sizeof head);
        flow+=dfs(s,inf);
    }
    return flow;
}
bool vis[N];
void mincut(int u)
{
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int v=e[i].to;
        if(!vis[v]&&e[i].c)
            mincut(v);
    }
}
void solve()
{
    cin>>m>>n;
    s=1,t=n;
    for(int i=1,c;i<=m;i++)
    {
        cin>>a[i]>>b[i]>>c;
        add(a[i],b[i],c);
        add(b[i],a[i],0);
    }
    //最小割的容量
    cout<<dinic()<<endl;
    //最小割的划分
    for(int i=1;i<=n;i++)   // S
        if(vis[i])
            cout<<i<<" ";
    for(int i=1;i<=n;i++)   // T
        if(!vis[i])
            cout<<i<<" ";
    //最小割最少边数
    idx=1;
    memset(head,0,sizeof head);
    for(int i=1;i<=m;i++)
    {
        add(a[i],b[i],1);
        add(b[i],a[i],0);
    }
    cout<<dinic()<<endl;
}
signed main()
{
    //ios;
    //int T;cin>>T;
    //while(T--)
        solve();
    return 0;
}


C. Directional Increase

思路:
1.可通过加一移动到下一个元素,也可通过减1操纵移动到前一个元素,由于是从1开始,最后回到位置1,因此前缀和必定是>=0的。
2.在最后后一个非0元素由于要回到1,因此肯定为负数,前缀和肯定为0.

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=1e6+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,a[N],sum[N];
void solve()
{
    cin>>n;
    for(int i=0;i<=n;i++)
        sum[i]=0;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    for(int i=1;i<=n;i++)
        sum[i]=sum[i-1]+a[i];
    int flag=1,idx=1;
    for(int i=n;i>=1;i--)
    {
        if(a[i])
        {
            idx=i;break;
        }
    }
    if(sum[n]) flag=0;
    for(int i=1;i<=n;i++)
    {
        if(sum[i]<=0&&i!=idx)
        {
            flag=0;break;
        }
        else if(i==idx)
            break;
    }
    if(flag)
        cout<<"Yes"<<endl;
    else
        cout<<"No"<<endl;
}
signed main()
{
    //ios;
    int T;cin>>T;
    while(T--)
        solve();
    return 0;
}


D. Fake Plastic Trees

思路:
1.贪心思想,需要对每个叶子结点都操作一次,本条路径上所有经过的点都加上该值。
2.利用深搜,若大于r[u],则返回r[u];若大于l[u],则返回累加值s
3.若都不满足,需要再进行一次操作,返回r[u]

#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define For(i,a,b) for(i=(a);i<=(b);++i)
#define ios (ios::sync_with_stdio(false),cin.tie(0),cout.tie(0))
using namespace std;
const int N=1e6+7;
const int inf=0x3f3f3f3f;
const int mod=1e9+7;
int n,cnt,head[N],l[N],r[N],ans;
struct node
{
    int to,nxt;
}e[N];
void add(int from,int to)
{
    e[++cnt].to=to;
    e[cnt].nxt=head[from];
    head[from]=cnt;
}
int dfs(int u,int fa)
{
    int s=0;
    for(int i=head[u];i;i=e[i].nxt)
    {
        int j=e[i].to;
        if(j==fa) continue;
        s+=dfs(j,u);
    }
    if(head[u]==-1)
    {
        ans++;return r[u];
    }
    if(s>=r[u]) return r[u];
    if(s>=l[u]) return s;
    ans++;
    return r[u];
}
void solve()
{
    cin>>n;
    cnt=ans=0;
    for(int i=0;i<=n;i++)
        head[i]=0;
    for(int i=2;i<=n;i++)
    {
        int x;cin>>x;
        add(x,i);
    }
    for(int i=1;i<=n;i++)
        cin>>l[i]>>r[i];
    dfs(1,0);
    cout<<ans<<endl;
}
signed main()
{
    //ios;
    int T;cin>>T;
    while(T--)
        solve();
    return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值