Codeforces 546E Soldier and Traveling 最大流 C#实现

题意

  • 在某个国家有n个城市,他们通过m条无向的道路相连。每个城市有一支队伍。第i个城市的队伍有ai个队员。现在队员开始移动。每个队员可以呆在原地,或者走到和他所在城市直接相邻的城市
  • 判断移动之后,能不能使得第i个城市恰好有bi个队员。若可以,需给出移动方式

思路

  • 将源点与各个点相连,容量就是a[i]。
  • 将汇点与各个点相连,容量就是b[i]。
  • 将i与i+n相连,容量是inf,表示队员可以留在自己的城市里面。
  • 对于两个城市i与j有边的,将i与j+n相连,将j与i+n相连,容量均为inf,表示队员可以移动
  • 最大流,判断是否等于sum_a 和 sum_b,不是为no,是为yes
  • 若是yes,通过图中加入的反向边流量可以得出队员如何移动。

实现

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DTATest
{
    class Program
    {
        static int n, m, suma = 0, sumb = 0;
        static MaxFlow mf;
        static int[] a = new int[105];
        static int[] b = new int[105];
        const int MAX = 300;
        static void Main(string[] args)
        {
            var str = Console.ReadLine().Split(' ');
            n = Int32.Parse(str[0]);
            m = Int32.Parse(str[1]);
            mf = new MaxFlow(MAX);
            mf.init(n * 2 + 2);
            var stra = Console.ReadLine().Split(' ');
            var strb = Console.ReadLine().Split(' ');
            for (int i = 0; i < n; i++)
            {
                a[i] = Int32.Parse(stra[i]);
                b[i] = Int32.Parse(strb[i]);
                suma += a[i];
                sumb += b[i];
                mf.add_edge(0, i + 1, a[i]);
                mf.add_edge(n + i + 1, mf.V - 1, b[i]);
                mf.add_edge(i + 1, n + i + 1, MaxFlow.INF);
            }
            for (int i = 0; i < m; i++)
            {
                str = Console.ReadLine().Split(' ');
                mf.add_edge(Int32.Parse(str[0]), Int32.Parse(str[1]) + n, MaxFlow.INF);
                mf.add_edge(Int32.Parse(str[1]), Int32.Parse(str[0]) + n, MaxFlow.INF);
            }
            if (mf.max_flow(0, mf.V - 1) == suma && sumb == suma)
            {
                Console.WriteLine("YES");
                for (int i = 0; i < n; i++)
                {
                    int[] tmp = new int[n];
                    //通过i号点的边的反向边来得到流量
                    for (int j = 0; j < mf.G[i + 1].Count; j++)
                    {
                        int v = mf.G[i + 1][j].to - n - 1;
                        int vid = mf.G[i + 1][j].to;
                        //如果是反向边,则跳过
                        if (vid == 0)
                            continue;
                        int eid = mf.G[i + 1][j].rev;
                        tmp[v] = mf.G[vid][eid].cap;
                    }
                    for (int j = 0; j < n - 1; j++)
                    {
                        Console.Write(tmp[j] + " ");
                    }
                    Console.WriteLine(tmp.Last());
                        
                }
            }
            else
            {
                Console.WriteLine("NO");
            }
            //Console.Read();
        }
    }


    /// <summary>
    /// 实现最大流的类,需实例化使用
    /// </summary>
    public class MaxFlow
    {
        #region 内部类定义
        /// <summary>
        /// 最大流边结构定义
        /// </summary>
        public class edge
        {
            public int to, cap, rev;
            public edge(int to, int cap, int rev)
            {
                this.to = to;
                this.cap = cap;
                this.rev = rev;
            }
        }
        #endregion


        #region 变量定义
        /// <summary>
        /// 最大节点数,可略大于真实节点数,以防访问越界
        /// </summary>
        private int MAX_V;
        /// <summary>
        /// 设置无穷大值
        /// </summary>
        public const int INF = 0x3f3f3f3f;
        /// <summary>
        /// 节点数
        /// </summary>
        public int V;
        /// <summary>
        /// 图的结构
        /// </summary>
        public List<edge>[] G;
        /// <summary>
        /// 顶点到源点的距离标号 
        /// </summary>
        public int[] level;
        /// <summary>
        /// 当前弧,在其之前的边已经没有用了
        /// </summary>
        public int[] iter;

        #endregion

        #region 构造函数
        public MaxFlow(int MAX_V)
        {
            this.MAX_V = MAX_V;
            V = 0;
            G = new List<edge>[this.MAX_V];
            for (int i = 0; i < G.Length; i++)
            {
                G[i] = new List<edge>();
            }
            level = new int[this.MAX_V];
            iter = new int[this.MAX_V];
        }

        #endregion

        #region 公有函数
        /// <summary>
        /// 加边前先初始化
        /// </summary>
        /// <param name="n">节点数量</param>
        public void init(int n)
        {
            for (int i = 0; i < V; i++)
            {
                G[i].Clear();
            }
            V = n;
        }
        /// <summary>
        /// 添加一条边
        /// </summary>
        /// <param name="from">起点</param>
        /// <param name="to">终点</param>
        /// <param name="cap">容量</param>
        public void add_edge(int from, int to, int cap)
        {
            G[from].Add(new edge(to, cap, G[to].Count));
            G[to].Add(new edge(from, 0, G[from].Count - 1));
        }
        /// <summary>
        /// 求解从s到t的最大流
        /// </summary>
        public int max_flow(int s, int t)
        {
            int flow = 0;
            for (; ; )
            {
                bfs(s);
                if (level[t] < 0)
                    return flow;
                fill(iter, 0);
                int f;
                while ((f = dfs(s, t, INF)) > 0)
                {
                    flow += f;
                }
            }
        }

        #endregion

        #region 私有方法
        private void fill(int[] a, int val)
        {
            for (int i = 0; i < a.Length; i++)
            {
                a[i] = val;
            }
        }

        /// <summary>
        /// 通过DFS寻找增广路
        /// </summary>
        private int dfs(int v, int t, int f)
        {
            if (v == t)
                return f;
            for (int i = iter[v]; i < G[v].Count; i++)
            {
                if (G[v][i].cap > 0 && level[v] < level[G[v][i].to])
                {
                    int d = dfs(G[v][i].to, t, Math.Min(f, G[v][i].cap));
                    if (d > 0)
                    {
                        G[v][i].cap -= d;
                        G[G[v][i].to][G[v][i].rev].cap += d;
                        return d;
                    }
                }
            }
            return 0;

        }

        /// <summary>
        /// 通过BFS计算从源点出发的距离标号
        /// </summary>
        void bfs(int s)
        {
            fill(level, -1);
            Queue<int> que = new Queue<int>();
            level[s] = 0;
            que.Enqueue(s);
            while (que.Count > 0)
            {
                int v = que.Dequeue();
                for (int i = 0; i < G[v].Count; i++)
                {
                    edge e = G[v][i];
                    if (e.cap > 0 && level[e.to] < 0)
                    {
                        level[e.to] = level[v] + 1;
                        que.Enqueue(e.to);
                    }
                }
            }
        }
        #endregion
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值