最大流,二分法,拆点法(士兵移动 uva 12264)

来源:互联网 发布:差评师数据设置 编辑:程序博客网 时间:2024/05/17 04:37

给n个点的无权无向图(n<=100),每个点有一个非负数ai。若ai==0则此点归敌方所有,若ai>0则此点归你且上面有ai个属于你的士兵。保证至少有一个属于你的点与敌方的点相邻。你可以让你的每个士兵最多移动一次,每次可以待在原地或者去到相邻的属于你的领地,但每个点至少要留1各士兵,使得最薄弱的关口尽量坚固。关口是指与敌方点相邻的点,薄弱与坚固分别指兵少与兵多。


我参考了这篇博客上的一些讲解。http://www.voidcn.com/blog/a197p/article/p-4252874.html

思路:把每个点拆成两个点,一个入度,一个出度,入度向自己的和每个相邻的点的出度连一条边,容量是ai,每个点出度连一条边到汇点,容量为1,那些与敌人相邻的点再多连一条边到汇点,容量是二分的值,我们只需要二分这个值,跑一下网络流,如果满流,表示可以,否则不行。


本篇通过第二个样例讲解思路,下图是第二个样例的建图结果。

敌方的点不需要参与建图,因此图中没有6,7号点。

INF指无穷,mid指二分的中值。


Q:为何需要拆点?

A:我们希望通过拆点来实现每个士兵最多移动一次。下图中每个INF的边都是士兵移动的边,观察后可以发现如此建图士兵只能从入点移动到另一个点的出点,因此最多只能移动一次。(根据题意每次最远移动到相邻的点)

Q:为何出点需要连一条容量为1的边到汇点?

A:为了保证己方的点至少有1个士兵,观察下图 1出 与 2出 满流时说明1和2点至少1个人。

Q:为何要二分?

A:我们要让最薄弱的关口尽量坚固,就得使关口的士兵分配得尽量均匀,然而最大流的算法不能保证分配均匀,我们只好一个个的试,满载就放宽条件,不满载就严加条件,直到试出可行的最大解。

以下渣代码

#include<stdio.h>#include<vector>#include<queue>#include<string.h>#define maxn 250#define INF 0X3F3F3F3Fusing namespace std;int N;int A[maxn];char MAP[maxn][maxn];bool ib[maxn];struct Edge{    int from,to,cap,flow;    Edge(int u,int v,int c,int f):from(u),to(v),cap(c),flow(f){}};struct EdmondsKarp{    int n,m;    vector<Edge>edges;    vector<int>G[maxn];    int a[maxn];    int p[maxn];    void init(int n)    {        this->n=n;        for(int i=0;i<n;i++) G[i].clear();        edges.clear();    }    void AddEdge(int from,int to,int cap)    {        edges.push_back(Edge(from,to,cap,0));        edges.push_back(Edge(to,from,0,0));        m=edges.size();        G[from].push_back(m-2);        G[to].push_back(m-1);    }    int Maxflow(int s,int t)    {        int flow=0;        for(;;)        {            memset(a,0,sizeof(a));            queue<int>Q;            Q.push(s);            a[s]=INF;            while(!Q.empty())            {                int x=Q.front();Q.pop();                for(unsigned int i=0;i<G[x].size();i++)                {                    Edge& e=edges[G[x][i]];                    if(!a[e.to]&&e.cap>e.flow)                    {                        p[e.to]=G[x][i];                        a[e.to]=min(a[x],e.cap-e.flow);                        Q.push(e.to);                    }                }                if(a[t]) break;            }            if(!a[t]) break;            for(int u=t;u!=s;u=edges[p[u]].from)            {                edges[p[u]].flow+=a[t];                edges[p[u]^1].flow-=a[t];            }            flow+=a[t];        }        return flow;    }    int Build(int val)    {        int ans=0;        memset(ib,0,sizeof(ib));        init(2*N+2);        for(int i=1;i<=N;i++)        {            if(!A[i]) continue;            AddEdge(0,i,A[i]);            AddEdge(i,i+N,A[i]);            for(int j=1;j<=N;j++)                if(MAP[i][j]=='Y')                {                    if(!A[j]) ib[i]=true;                    else AddEdge(i,j+N,INF);                }        }        for(int i=1;i<=N;i++)            if(ib[i]) {AddEdge(i+N,n-1,val);ans+=val;}            else if(A[i]){AddEdge(i+N,n-1,1);ans++;};        return ans;    }    void solve()    {        int a,b,ans;        int l=0,r=10010;        while(l<r)        {            int mid=(l+r)>>1;            a=Build(mid);            b=Maxflow(0,n-1);            if(a==b)            {                l=mid+1;                ans=mid;            }            else r=mid;        }        printf("%d\n",ans);    }};int main(){    int t;    scanf("%d",&t);    while(t--)    {        scanf("%d",&N);        for(int i=1;i<=N;i++) scanf("%d",&A[i]);        for(int i=1;i<=N;i++) scanf("%s",MAP[i]+1);        EdmondsKarp EK;        EK.solve();    }    return 0;}


0 0
原创粉丝点击