NOI2018湖北省队集训Day1 T3 san

题面:
这里写图片描述
这里写图片描述


得分情况:
本来是照着30分的 2 n 枚举全排列打的,结果多拿了五分,开心。


正解:
题目需要我们求的是一个拓扑序中的一个子串的和的最大值,看到题目并没有什么思路而且数据范围又这么小,开始考虑网络流。
我们对于每个节点考虑三种情况,1.不选在选的子串前,2.在选的子串中,3.不选在选的子串后。然后把每个点拆成两个点,从s到t依次连边,会有三条边,即对应这三种情况。如果一个点的权值为正,那么我们先将它加入答案,考虑最小割模型,然后1,3的flow为权值(不选的话答案减权值),2的flow为0(选的话对答案无影响);如果一个点权值为负,1,3的flow为0(不选的话对答案无影响),2的flow为权值的相反数(选的话答案减权值)。
我们再来考虑拓扑序的影响。其实两个节点之间的边就是限制了它们在拓扑序中出现的先后顺序,那么我们只要从起点拆成的两个点分别向终点拆成的两个点连flow为inf的边就行了。
最后跑最小割(最大流)即可。


代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn=60,inf=1e8;
struct stu
{
    int to,next,flow;
}road[1000000]; int first[maxn*3],cnt=1;
int dep[maxn*3];
int n,m,s,t,ans;
queue <int> q;

void addedge(int x,int y,int flow)
{
    road[++cnt].to=y;
    road[cnt].flow=flow;
    road[cnt].next=first[x];
    first[x]=cnt;

    road[++cnt].to=x;
    road[cnt].flow=0;
    road[cnt].next=first[y];
    first[y]=cnt;
}

bool bfs()
{
    memset(dep,0,sizeof(dep));
    while(!q.empty()) q.pop();
    dep[s]=1;
    q.push(s);
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(int i=first[now];i;i=road[i].next)
        {
            int to=road[i].to;
            if(!dep[to]&&road[i].flow>0)
            {
                dep[to]=dep[now]+1;
                q.push(to);
            }
        }
    }
    if(dep[t]==0) return 0;
    return 1;
}

int dfs(int now,int maxx)
{
    if(now==t) return maxx;
    for(int i=first[now];i;i=road[i].next)
    {
        int to=road[i].to;
        if(road[i].flow>0&&dep[to]==dep[now]+1)
        {
            int k=dfs(to,min(maxx,road[i].flow));
            if(k>0)
            {
                road[i].flow-=k;
                road[i^1].flow+=k;
                return k;
            }
        }
    }
    return 0;
}

int min_cut()
{
    int nowans=0;
    while(bfs())
    {
        while(int k=dfs(s,inf)) nowans+=k;
    }
    return nowans;
}

int main()
{
    scanf("%d%d",&n,&m);
    s=0,t=2*n+1;
    for(int i=1;i<=n;i++)
    {
        int x;
        scanf("%d",&x);
        if(x>=0)
        {
            ans+=x;
            addedge(s,i,x);
            addedge(i,i+n,0);
            addedge(i+n,t,x);
        }
        else
        {
            addedge(s,i,0);
            addedge(i,i+n,-x);
            addedge(i+n,t,0);
        }
    }
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        addedge(x,y,inf);
        addedge(x+n,y+n,inf);
    }
    printf("%d\n",ans-min_cut());
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39662197/article/details/80416086
SAN