SPOJ 839 Optimal Marks

来源:互联网 发布:ubuntu on windows 编辑:程序博客网 时间:2024/06/05 16:30

最小割的经典题。

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

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

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

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

如果能让所有的点都在一个集合中那是最好的,因为所有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;}