1. 程式人生 > >P1035 棋盤覆蓋 (二分圖匹配、最大網路流)

P1035 棋盤覆蓋 (二分圖匹配、最大網路流)

描述

給出一張n*n(n<=100)的國際象棋棋盤,其中被刪除了一些點,問可以使用多少1*2的多米諾骨牌進行掩蓋。

輸入格式

第一行為n,m(表示有m個刪除的格子)
第二行到m+1行為x,y,分別表示刪除格子所在的位置
x為第x行
y為第y列 

輸出格式

一個數,即最大覆蓋格數

二分圖最大匹配

二維的棋盤可以轉換為一維來表示,比如8*8棋盤上的點(6,5),轉換為相應的一維id=44,點(x,y)轉換公式為id=(x-1)*n+y-1 。

首先給用bfs或者dfs給棋盤染色,相鄰的點染成不同的顏色,忽略被刪除的點,dfs會爆棧,這裡使用bfs。

特別注意例如以下情況,若bfs或dfs只以找到的第一個未刪除的點為起點進行,則無法將如圖“中間”的點進行正確染色導致匹配數不正確。

OO OOOO

OX XXXO

OX OOXO

OX OOXO

OX XXXO

OO OOOO

-------------------------------------------------------------------------------------------------------------------------------------------------------------

1.使用HopcroftKarp演算法,遍歷棋盤,給顏色不同且相鄰的點新增一條邊,所求就是最大匹配數。O((E + V) sqrt(V))

------------------------------------------------------------------------------------------------------------------------------------------

package cn.UnitTest;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
import java.util.Stack;

public class Main
{
	public static void main(String[] args)
	{
		cover1 cov=new cover1();
		System.out.println(cov.getmaxmatch());
	
	}
}

class cover1
{
	private int n;
	private int m;
	private boolean[] visited;
	private boolean[] isdelete;
	private boolean[] color;
	private HopcroftKarp h;
	private ArrayDeque<storexy> deque=new ArrayDeque<>();
	public cover1()
	{
		Scanner cin=new Scanner(System.in);
		n=cin.nextInt();
		m=cin.nextInt();
		isdelete=new boolean[n*n];
		visited=new boolean[n*n];
		color=new boolean[n*n];
		for(int i=0;i<m;i++)
		{
			int x=cin.nextInt();
			int y=cin.nextInt();
			isdelete[toid(x, y)]=true;
		}
L1:		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				{
					int id=toid(i, j);
					if(!isdelete[id])
						{
							color[id]=true;
							deque.add(new storexy(i, j));
							break L1;
						}
				}
		
		while(!deque.isEmpty())
		{
			storexy xy=deque.pollFirst();
			bfs(xy.getx(),xy.gety());
		}
		
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				{
					int id=toid(i, j);
					if(!isdelete[id]&&!visited[id])
						{
							color[id]=true;
							deque.add(new storexy(i, j));
						}
				}
		
		while(!deque.isEmpty())
		{
			storexy xy=deque.pollFirst();
			bfs(xy.getx(),xy.gety());
		}
		
		
		Graph G=new Graph(n*n);
		
		
		visited=new boolean[n*n];
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				if(!isdelete[toid(i, j)]&&color[toid(i, j)]==true)
				{
					int[] xoff={-1,1,0,0};
					int[] yoff={0,0,1,-1};
					for(int k=0;k<4;k++)
					{
						int xx=i+xoff[k];
						int yy=j+yoff[k];
						if(xx>=1&&xx<=n&&yy>=1&&yy<=n)
						{
							int id=toid(xx, yy);
							if(!isdelete[id]&&color[id]==false)
								G.addEdge(toid(i, j), id);
						}
					}
				}

			}
		
		h=new HopcroftKarp(G);
	}
	
	private int toid(int x,int y)
	{
		return (x-1)*n+y-1;
	}
	
	private void bfs(int x,int y)
	{
		visited[toid(x, y)]=true;
		boolean colornow=color[toid(x, y)];
		int[] xoff={-1,1,0,0};
		int[] yoff={0,0,1,-1};
		for(int i=0;i<4;i++)
		{
			int xx=x+xoff[i];
			int yy=y+yoff[i];
			if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!isdelete[toid(xx, yy)]&&!visited[toid(xx, yy)])
			{
				color[toid(xx, yy)]=!colornow;
				storexy xy=new storexy(xx, yy);
				if(!deque.contains(xy))
				deque.add(xy);
			}
		}
	}
	
	public int getmaxmatch()
	{
		return h.size();
	}
}

class storexy
{
	private int x;
	private int y;
	public storexy(int x,int y)
	{
		this.x=x;
		this.y=y;
	}
	@Override
	public boolean equals(Object obj)
	{
		
		// TODO Auto-generated method stub
		return (x==((storexy)obj).getx()&&y==((storexy)obj).gety());
	}
	public int getx()
	{
		return x;
	}
	public int gety()
	{
		return y;
	}
}


class HopcroftKarp
{
	private static final int UNMATCHED = -1;

	private final int V;
	private BipartiteX bipartition;
	private int cardinality;
	private int[] mate;
	private boolean[] inMinVertexCover;
	private boolean[] marked;
	private int[] distTo;

	public HopcroftKarp(Graph G)
	{
		bipartition = new BipartiteX(G);
		if (!bipartition.isBipartite())
		{
			throw new IllegalArgumentException("graph is not bipartite");
		}

		this.V = G.V();
		mate = new int[V];
		for (int v = 0; v < V; v++)
			mate[v] = UNMATCHED;

		while (hasAugmentingPath(G))
		{
			@SuppressWarnings("unchecked")
			Iterator<Integer>[] adj = (Iterator<Integer>[]) new Iterator[G.V()];
			for (int v = 0; v < G.V(); v++)
				adj[v] = G.adj(v).iterator();

			for (int s = 0; s < V; s++)
			{
				if (isMatched(s) || !bipartition.color(s))
					continue;

				Stack<Integer> path = new Stack<Integer>();
				path.push(s);
				while (!path.isEmpty())
				{
					int v = path.peek();

					if (!adj[v].hasNext())
						path.pop();

					else
					{

						int w = adj[v].next();
						if (!isLevelGraphEdge(v, w))
							continue;

						path.push(w);

						if (!isMatched(w))
						{

							while (!path.isEmpty())
							{
								int x = path.pop();
								int y = path.pop();
								mate[x] = y;
								mate[y] = x;
							}
							cardinality++;
						}
					}
				}
			}
		}

		inMinVertexCover = new boolean[V];
		for (int v = 0; v < V; v++)
		{
			if (bipartition.color(v) && !marked[v])
				inMinVertexCover[v] = true;
			if (!bipartition.color(v) && marked[v])
				inMinVertexCover[v] = true;
		}

	}

	private boolean isLevelGraphEdge(int v, int w)
	{
		return (distTo[w] == distTo[v] + 1) && isResidualGraphEdge(v, w);
	}

	private boolean isResidualGraphEdge(int v, int w)
	{
		if ((mate[v] != w) && bipartition.color(v))
			return true;
		if ((mate[v] == w) && !bipartition.color(v))
			return true;
		return false;
	}

	private boolean hasAugmentingPath(Graph G)
	{

		marked = new boolean[V];
		distTo = new int[V];
		for (int v = 0; v < V; v++)
			distTo[v] = Integer.MAX_VALUE;

		ArrayDeque<Integer> queue = new ArrayDeque<Integer>();
		for (int v = 0; v < V; v++)
		{
			if (bipartition.color(v) && !isMatched(v))
			{
				queue.add(v);
				marked[v] = true;
				distTo[v] = 0;
			}
		}

		boolean hasAugmentingPath = false;
		while (!queue.isEmpty())
		{
			int v = queue.pollFirst();
			for (int w : G.adj(v))
			{

				if (isResidualGraphEdge(v, w))
				{
					if (!marked[w])
					{
						distTo[w] = distTo[v] + 1;
						marked[w] = true;
						if (!isMatched(w))
							hasAugmentingPath = true;

						if (!hasAugmentingPath)
							queue.add(w);
					}
				}
			}
		}

		return hasAugmentingPath;
	}

	public int mate(int v)
	{
		validate(v);
		return mate[v];
	}

	public boolean isMatched(int v)
	{
		validate(v);
		return mate[v] != UNMATCHED;
	}

	public int size()
	{
		return cardinality;
	}

	public boolean isPerfect()
	{
		return cardinality * 2 == V;
	}

	public boolean inMinVertexCover(int v)
	{
		validate(v);
		return inMinVertexCover[v];
	}

	private void validate(int v)
	{
		if (v < 0 || v >= V)
			throw new IndexOutOfBoundsException("vertex " + v
					+ " is not between 0 and " + (V - 1));
	}
}


class BipartiteX
{
	private static final boolean WHITE = false;
	private boolean isBipartite; 
	private boolean[] color; 								
	private boolean[] marked; 
	private int[] edgeTo; 
	private ArrayDeque<Integer> cycle; 

	public BipartiteX(Graph G)
	{
		isBipartite = true;
		color = new boolean[G.V()];
		marked = new boolean[G.V()];
		edgeTo = new int[G.V()];

		for (int v = 0; v < G.V() && isBipartite; v++)
		{
			if (!marked[v])
			{
				bfs(G, v);
			}
		}
		assert check(G);
	}

	private void bfs(Graph G, int s)
	{
		ArrayDeque<Integer> q = new ArrayDeque<Integer>();
		color[s] = WHITE;
		marked[s] = true;
		q.add(s);

		while (!q.isEmpty())
		{
			int v = q.pollFirst();
			for (int w : G.adj(v))
			{
				if (!marked[w])
				{
					marked[w] = true;
					edgeTo[w] = v;
					color[w] = !color[v];
					q.add(w);
				} else if (color[w] == color[v])
				{
					isBipartite = false;

					cycle = new ArrayDeque<>();
					Stack<Integer> stack = new Stack<Integer>();
					int x = v, y = w;
					while (x != y)
					{
						stack.push(x);
						cycle.add(y);
						x = edgeTo[x];
						y = edgeTo[y];
					}
					stack.push(x);
					while (!stack.isEmpty())
						cycle.add(stack.pop());
					cycle.add(w);
					return;
				}
			}
		}
	}

	public boolean isBipartite()
	{
		return isBipartite;
	}

	public boolean color(int v)
	{
		if (!isBipartite)
			throw new UnsupportedOperationException("Graph is not bipartite");
		return color[v];
	}

	public Iterable<Integer> oddCycle()
	{
		return cycle;
	}

	private boolean check(Graph G)
	{
		if (isBipartite)
		{
			for (int v = 0; v < G.V(); v++)
			{
				for (int w : G.adj(v))
				{
					if (color[v] == color[w])
					{
						System.err
								.printf("edge %d-%d with %d and %d in same side of bipartition\n",
										v, w, v, w);
						return false;
					}
				}
			}
		}

		else
		{
			int first = -1, last = -1;
			for (int v : oddCycle())
			{
				if (first == -1)
					first = v;
				last = v;
			}
			if (first != last)
			{
				System.err.printf("cycle begins with %d and ends with %d\n",
						first, last);
				return false;
			}
		}
		return true;
	}

}

class Graph
{
	private final int V;
	private int E;
	private ArrayList<Integer>[] adj;

	@SuppressWarnings("unchecked")
	public Graph(int V)
	{
		this.V = V;
		this.E = 0;
		adj = (ArrayList<Integer>[]) new ArrayList[V];
		for (int v = 0; v < V; v++)
			adj[v] = new ArrayList<>();
	}

	public Graph(Scanner cin)
	{
		this(cin.nextInt());
		int E = cin.nextInt();
		for (int i = 0; i < E; i++)
		{
			int v = cin.nextInt();
			int w = cin.nextInt();
			addEdge(v, w);
		}
	}

	public void addEdge(int v, int w)
	{
		adj[v].add(w);
		adj[w].add(v);
		this.E++;
	}

	public int V()
	{
		return V;
	}

	public int E()
	{
		return E;
	}

	public ArrayList<Integer> adj(int v)
	{
		return adj[v];
	}

	public int degree(int v)
	{
		return adj[v].size();
	}

}

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

2.使用最大網路流法,人為地新增起點和終點,選擇黑色頂點,如果有與其相鄰的白色頂點,則新增一條權重為1的由黑色頂點指向白色的有向邊,若黑色頂點數有m個,白色頂點數有K個,則新增起點指向黑色頂點權重為1的m條有向邊,新增白色頂點指向終點的權重為1的K條有向邊,使用Ford演算法求起點到終點的最大網路流量。O(VE^2)

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Scanner;



public class Main
{
	public static void main(String[] args)
	{
		cover1 cov=new cover1();
		System.out.println(cov.getmaxmatch());
	
	}
}

class cover1
{
	private int n;
	private int m;
	private boolean[] visited;
	private boolean[] isdelete;
	private boolean[] color;
	private FordFulkerson fordfulkerson;
	private ArrayDeque<storexy> deque=new ArrayDeque<>();
	public cover1()
	{
		Scanner cin=new Scanner(System.in);
		n=cin.nextInt();
		m=cin.nextInt();
		isdelete=new boolean[n*n];
		visited=new boolean[n*n];
		color=new boolean[n*n];
		for(int i=0;i<m;i++)
		{
			int x=cin.nextInt();
			int y=cin.nextInt();
			isdelete[toid(x, y)]=true;
		}
L1:		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				{
					int id=toid(i, j);
					if(!isdelete[id])
						{
							color[id]=true;
							deque.add(new storexy(i, j));
//							bfs(i, j);
							break L1;
						}
				}
		while(!deque.isEmpty())
		{
			storexy xy=deque.pollFirst();
			bfs(xy.getx(),xy.gety());
		}
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
				{
					int id=toid(i, j);
					if(!isdelete[id]&&!visited[id])
						{
							color[id]=true;
							deque.add(new storexy(i, j));
						}
				}
		
		while(!deque.isEmpty())
		{
			storexy xy=deque.pollFirst();
			bfs(xy.getx(),xy.gety());
		}
		
		FlowNetwork flownetwork=new FlowNetwork(n*n+2);
		
		
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				if(!isdelete[toid(i, j)]&&color[toid(i, j)]==true)
				{
					flownetwork.addEdge(new FlowEdge(n*n, toid(i, j), 1.0));
					int[] xoff={-1,1,0,0};
					int[] yoff={0,0,1,-1};
					for(int k=0;k<4;k++)
					{
						int xx=i+xoff[k];
						int yy=j+yoff[k];
						if(xx>=1&&xx<=n&&yy>=1&&yy<=n)
						{
							int id=toid(xx, yy);
							if(!isdelete[id]&&color[id]==false)
								flownetwork.addEdge(new FlowEdge(toid(i, j), id, 1.0));
						}
					}
				}
				else if(!isdelete[toid(i, j)]&&color[toid(i, j)]==false)
					flownetwork.addEdge(new FlowEdge(toid(i, j), n*n+1, 1.0));
			}
		
		fordfulkerson=new FordFulkerson(flownetwork, n*n, n*n+1);
		
		// TODO Auto-generated constructor stub
	}
	
	private int toid(int x,int y)
	{
		return (x-1)*n+y-1;
	}
	
	private void bfs(int x,int y)
	{
		visited[toid(x, y)]=true;
		boolean colornow=color[toid(x, y)];
		int[] xoff={-1,1,0,0};
		int[] yoff={0,0,1,-1};
		for(int i=0;i<4;i++)
		{
			int xx=x+xoff[i];
			int yy=y+yoff[i];
			if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!isdelete[toid(xx, yy)]&&!visited[toid(xx, yy)])
			{
				color[toid(xx, yy)]=!colornow;
				storexy xy=new storexy(xx, yy);
				if(!deque.contains(xy))
				deque.add(xy);
	//			bfs(xx, yy);
			}
		}
	}
	
	public int getmaxmatch()
	{
		return (int) fordfulkerson.value();
	}
	
	
}

class FlowEdge
{
	private final int v;
	private final int w;
	private final double capacity;
	private double flow;
	public FlowEdge(int v,int w,double capacity)
	{
		this.v=v;
		this.w=w;
		this.capacity=capacity;
		this.flow=0.0;
		// TODO Auto-generated constructor stub
	}
	public int from()
	{
		return v;
	}
	public int to()
	{
		return w;
	}
	public double capacity()
	{
		return capacity;
	}
	public double flow()
	{
		return flow;
	}
	
	public int other(int vertex)
	{
		if(vertex==v)
			return w;
		if(vertex==w)
			return v;
		throw new RuntimeException("Inconsistant Edge");
	}
	
	public double residualCapacityTo(int vertex)
	{
		if(vertex==v)
			return flow;
		else if(vertex==w)
			return capacity-flow;
		else {
			throw new RuntimeException("Inconsistant Edge");
		}
	}
	public void addResidualFlowTo(int vertex,double delta)
	{
		if(vertex==v)
			flow-=delta;
		else if(vertex==w)
			flow+=delta;
	}
	public String toString()
	{
		return String.format("%d->%d %.2f %.2f", v,w,capacity,flow);
	}
}

class FlowNetwork
{
	private final int V;
	private int E;
	private ArrayList<FlowEdge>[] adj;
	public FlowNetwork(int v)
	{
		this.V=v;
		this.E=0;
		adj=new ArrayList[V];
		for(int i=0;i<v;i++)
			adj[i]=new ArrayList<>();
		// TODO Auto-generated constructor stub
	}
	public FlowNetwork(Scanner cin)
	{
		this(cin.nextInt());
		this.E=cin.nextInt();
		for(int i=0;i<E;i++)
		{
			int v=cin.nextInt();
			int w=cin.nextInt();
			double capacity=cin.nextDouble();
			addEdge(new FlowEdge(v, w, capacity));
			
		}
		// TODO Auto-generated constructor stub
	}
	public void addEdge(FlowEdge e)
	{
		int v=e.from();
		int w=e.to();
		adj[v].add(e);
		adj[w].add(e);
	}
	
	public int V()
	{
		return V;
	}
	public int E()
	{
		return E;
	}
	public Iterable<FlowEdge> adj(int v)
	{
		return adj[v];
	}
	
	public Iterable<FlowEdge> edges()
	{
		ArrayList<FlowEdge> list=new ArrayList<>();
		for(int v=0;v<V;v++)
			for(FlowEdge e:adj(v))
				if(e.to()!=v)
					list.add(e);
		return list;
	}
	
}


class FordFulkerson
{
	private boolean[] marked;
	private FlowEdge[] edgeTo;
	private double value;
	public FordFulkerson(FlowNetwork G,int s,int t)
	{
		while(hasAugmentingPath(G, s, t))
		{
			double bottle=Double.POSITIVE_INFINITY;
			for(int v=t;v!=s;v=edgeTo[v].other(v))
				bottle=Math.min(bottle, edgeTo[v].residualCapacityTo(v));
			for(int v=t;v!=s;v=edgeTo[v].other(v))
				edgeTo[v].addResidualFlowTo(v, bottle);
			value+=bottle;
			
		}
		// TODO Auto-generated constructor stub
	}
	
	public double value()
	{
		return value;
	}
	
	public boolean inCut(int v)
	{
		return marked[v];
	}
	
	
	private boolean hasAugmentingPath(FlowNetwork G,int s,int t)
	{
		marked=new boolean[G.V()];
		edgeTo=new FlowEdge[G.V()];
		ArrayDeque<Integer> q=new ArrayDeque<>();
		marked[s]=true;
		q.add(s);
		while(!q.isEmpty())
		{
			int v=q.pollFirst();
			for(FlowEdge e:G.adj(v))
			{
				int w=e.other(v);
				if(e.residualCapacityTo(w)>0&&!marked[w])
				{
					edgeTo[w]=e;
					marked[w]=true;
					q.add(w);
				}
			}
		}
		return marked[t];
	}
}

class storexy
{
	private int x;
	private int y;
	public storexy(int x,int y)
	{
		this.x=x;
		this.y=y;
		// TODO Auto-generated constructor stub
	}
	@Override
	public boolean equals(Object obj)
	{
		
		// TODO Auto-generated method stub
		return (x==((storexy)obj).getx()&&y==((storexy)obj).gety());
	}
	public int getx()
	{
		return x;
	}
	public int gety()
	{
		return y;
	}
}