SPOJ 839 Optimal Marks

最小割的经典题。

这题的具体做法就不说了,网上一大堆说的很好的。

我说一下怎么理解这题里的最小割吧。

一条边如果在最小割之中,你可以理解成让这条边左边的点与右边的点矛盾所需要的代价。

下面的讨论都是限定在某一个二进制位上:

如果能让所有的点都在一个集合中那是最好的,因为所有XOR的结果都是0。
但这明显是不可能的,因为根据题目所给的已知数字,已经分成了两部分了,即这个二进制位是0还是1。
默认左边集合是1,右边集合是0。
如果一个数字只需要和左边集合XOR,那么它显然只要为1就可以了,只和右边的话显然为0。
但是有些数字是和左右都需要XOR,这里就需要做出和左边相反还是右边相反的选择了,自己画一个图,如果它需要和左边两个点XOR,右边一个点XOR,那么这个最小割显然是右边那条边。
那么就可以理解最小割了。即和最少的人相反。
再扩大点范围,就是让左边集合与右边集合相交界处的权值最小。这也和最小割的原本定义类似。

然后这题的点数好像很坑,反正记得开大点。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>

using namespace std;
const int MAXN = 50100;
const int MAXM = 50000;
const int INF = 0x3f3f3f3f;
struct Edge
{
    int to,cap,flow,nex;
    Edge() {}
    Edge(int to,int cap,int flow,int nex):to(to),cap(cap),flow(flow),nex(nex) {}
} edge[MAXM];

int tol,dis[MAXN],n,m,k,head[MAXN],x[MAXN],y[MAXN],num[MAXN],ans[MAXN];
bool vis[MAXN];

void addedge(int u,int v,int cap,int rw=0)
{
    edge[tol]=Edge(v,cap,0,head[u]);
    head[u]=tol++;
    edge[tol]=Edge(u,rw,0,head[v]);
    head[v]=tol++;
}

bool bfs(int s,int t)
{
    queue<int> que;
    que.push(s);
    memset(dis,-1,sizeof dis);
    dis[s]=0;
    while (!que.empty())
    {
        int u=que.front();
        que.pop();
        for (int i=head[u]; ~i; i=edge[i].nex)
        {
            int v=edge[i].to;
            if (dis[v]==-1 && edge[i].cap>edge[i].flow)
            {
                dis[v] = dis[u]+1;
                if (v==t) return true;
                que.push(v);
            }
        }
    }
    return false;
}

int dfs(int u,int t,int cap)
{
    if (u==t) return cap;
    int flow=0,f;
    for (int i=head[u]; ~i; i=edge[i].nex)
    {
        int v=edge[i].to;
        if (dis[v]==dis[u]+1 && edge[i].cap>edge[i].flow)
        {
            f=dfs(v,t,min(cap-flow,edge[i].cap-edge[i].flow));
            edge[i].flow += f;
            edge[i^1].flow -=f;
            flow += f;
            if (flow == cap) break;
        }
    }
    if (!flow ) dis[u]=-1;
    return flow;
}

int dicnic(int s,int t)
{
    int flow=0,f;
    while (bfs(s,t))
        while ((f=dfs(s,t,INF))>0)
            flow += f;
    return flow;
}

void dfs2(int u,int ad)
{
    vis[u]=true;
    for (int i=head[u]; ~i; i=edge[i].nex)
    {
        int v=edge[i].to;
        if (!vis[v] && edge[i].cap>edge[i].flow)
        {
            ans[v] += ad;
            dfs2(v,ad);
        }
    }
}

int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        memset(ans,0,sizeof ans);
        memset(num,-1,sizeof num);
        scanf("%d%d",&n,&m);
        for (int i=1; i<=m; i++) scanf("%d%d",&x[i],&y[i]);
        scanf("%d",&k);
        for (int i=1; i<=k; i++)
        {
            int m;
            scanf("%d",&m);
            scanf("%d",&num[m]);
        }
        int ad=1;
        while (1)
        {
            memset(vis,0,sizeof vis);
            memset(head,-1,sizeof head);
            tol=0;
            bool flag=false;
            for (int i=1; i<=n; i++)
            {
                if (num[i]==-1) continue;
                if (num[i]>=1) flag=true;
                if (num[i]&1) addedge(0,i,INF);
                else addedge(i,n+1,INF);
                num[i]>>=1;
            }
            for (int i=1; i<=m; i++) addedge(x[i],y[i],1,1);
            if (!flag) break;
            dicnic(0,n+1);
            dfs2(0,ad);
            ad<<=1;
        }
        for (int i=1; i<=n; i++)
        {
            printf("%d\n",ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/z631681297/article/details/77985877