元旦老人与丛林

作死写一遍没保存

这题很经典大概吧,收获很多

  • 退流:

    网络流中如果要删去(u,v)边,则可以跑dinic(t,v,inf),dinic(u,s,inf),从而达到退流的效果,然后在把u->v的正反向边权设为0,达到删去边的效果。注意如果有最大流相关的操作,不要忘记maxflow加或减退的流量

  • 最大权闭合子图:

    如果一个图,需要选择的边对应的点都包含在图里面,则成为这个图的子图。每个点有正负权值,需要选取最大的权值的子图则成为最大权闭合子图。可以把S连向正权值的点,负权值点连向T,点与点如果有边权值为inf,显然,最小割必然在连向S或T的边中,而最小割|最大流就是问题的答案。

    对应的还有最大密度子图。

  • 对于这题:

    关键是得出 E-2*V<-2 则问题才有解这个结论,边变为点权为1,点权为-2,枚举每一个点,把该点权值变为0,依次求最大闭合子图,如果存在不满足等式条件,输出No return就完事了。

代码

#include<bits/stdc++.h>
using namespace std;
const int N=8e3+5,inf=1e9+5;
int n,m,x,y;
int totn,source,target;
int dis[N],flow[N],pos[N];
bool use[N];
int maxflow;
int level[N];
struct node
{
    int link;
    int w;
    int position;
};
vector<node> f[N];
void add_edge(int x,int y,int z)
{
    f[x].push_back((node){y, z,(int)f[y].size()});
    f[y].push_back((node){x, 0,(int)f[x].size()-1});// 获取x在y的第几条边
}
int dfs(int x,int flow)
{
    int ans = 0;
    if(x==target||flow==0)
        return flow;
    for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
    {
        if(i.w>0&&level[i.link]==level[x]+1)// 保证边权不为0 且按照深度dfs
        {
            int d = dfs(i.link, min(flow, i.w)); // 搜索,flow是最小的所以是min
            if(d==0)
                level[i.link] = 0;
            if(d>0)
            {
                i.w -= d;//正向边减少
                f[i.link][i.position].w += d;// 反向边增加
                flow -= d;
                ans += d;
                if(flow==0)
                    break;
            }
        }
    }
    return ans;
}
bool bfs(int s,int t)
{
    queue <int> q;
    q.push(s);
    memset(level, -1, sizeof(level));
    level[s] = 0;
    while(!q.empty())
    {
        int temp = q.front();
        q.pop();
        for(auto &i:f[temp])
        {
            if(i.w==0||level[i.link]!=-1)
                continue;
            level[i.link] = level[temp] + 1;
            q.push(i.link);
        }
    }
    if(level[t]!=-1)
        return 1;
    return 0;
}
int dinic(int s,int t,int flow)
{
    long long ans = 0;
    while(bfs(s,t))
        ans += dfs(s,flow);
    
    return ans;
}
int main()
{  
    scanf("%d %d",&n,&m);
    source=0;
    target=n+m+1;
    bool flag=0;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d %d",&x,&y);
        if(x==y)
        {
            flag=1;
            continue;
        }
        add_edge(i+n,y,inf);
        add_edge(i+n,x,inf);
    }
    if(flag==1)
    {
        printf("No\n");
        return 0;
    }
    for(int i=1;i<=m;i++)
        add_edge(source,i+n,1);
    for(int i=1;i<=n;i++)
        add_edge(i,target,2);

    // for(int i=0;i<=m+n+1;i++)
    // {
    //     printf("i:%d\n",i);
    //     for(auto j:f[i])
    //         printf("???%d %d",j.link,j.w);
    //     printf("\n");
    // }
    int maxflow=dinic(source,target,inf);
    if(m-maxflow>0)
    {
        printf("No\n");
        return 0;
    }
    for(int i=1;i<=n;i++)
    {
        // source=n+m+1;
        // target=n+m+1;
        // maxflow-=dinic(source,target,inf);
        source=i;
        target=0;
        maxflow-=dinic(i,0,inf);
        (f[i][int(f[i].size())-1]).w=0;
        (f[n+m+1][f[i][(int)f[i].size()-1].position]).w=0;
        source=0;
        target=n+m+1;
        maxflow+=dinic(0,n+m+1,inf);
        if(m-maxflow>0)
        {
            printf("No\n");
            return 0;
        }
        f[i][int(f[i].size())-1].w=2;
    }
    printf("Yes\n");
    return 0;   
}
/*
5 4
1 2
2 3
1 5
2 4
*/
$道路千万条,点赞第一条;阅读不规范,笔者两行泪$
posted @ 2021-01-25 23:50  Sakura_Momoko  阅读(65)  评论(0编辑  收藏  举报