poj 2843 Cutting Cake

题意:一块N*N大的蛋糕M次讯问,每次切走一个矩形,问每次切的矩形被分成了多少块(1 <= N <= 1000, 1 <= M <= 10000)。

解法:显然是求某个区域内的连通分量数目,由于这个区域内的元素在访问后会被切除,因此每个元素只被访问一次,因此可以通过dfs或bfs求得某个区域内的连通分量的个数,均摊复杂度。

但是每次寻找遍历的起始点(即区域内未被切走的蛋糕)复杂度为O(N^2),因此需要优化,最坏情况是怎么产生的呢?当某个区域内有连续的已经切走的蛋糕,那我们在寻找未切走的蛋糕时需要逐一判断,产生复杂度,因此如果遇到连续的切走的区间,如果能够在短时间内跳过它,那么复杂度就可以得到控制。类似于supermarket的那个题目,查询未被占用的最左元素,我们用并查集维护。对于每一行维护一个并查集,f[x]代表x开始连续的被切走的蛋糕的最右边的那块,当某块蛋糕杯切走且它右边相邻的也被切走,就将他合并到右边的元素,这样每次查询寻找遍历起点的复杂度就变为O(N)。

注:dfs会爆栈,显然应该使用bfs

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;

public class CutingCake2843 {
	class Union {
		int f[];
		Union(int n) {
			f= new int[n+10];
			for (int i = 1; i <= n; i++)
				f[i] = i;
		}
		int find(int x) {
			if (x != f[x])
				f[x] = find(f[x]);
			return f[x];
		}
	}
	class node{
		int x,y;
		node(int x,int y){
			this.x=x;
			this.y=y;
		}
	}
	node stack[]=new node[1010*1010];
	boolean vis[][]=new boolean[1010][1010];
	int minx, miny, maxx, maxy, n,top;
	boolean test(int x,int y){
		if (x <minx|| x>maxx||y<miny||y>maxy)
			return false;
		if (vis[x][y])
			return false;
		stack[top++]=new node(x,y);
		vis[x][y]=true;
		return true;
	}
	void bfs(int x, int y) {
		top=0;
		stack[top++]=new node(x,y);
		while(top!=0){
			x=stack[--top].x;
			y=stack[top].y;
		    vis[x][y] = true;
		    if(vis[x][y+1])
			   set[x].f[y]=y+1;
		    if(vis[x][y-1])
			   set[x].f[y-1]=y;
		   test(x,y+1);		    	
		   test(x,y-1);		    	
		   test(x-1,y);		    	
		   test(x+1,y); 
		}
	}

	StreamTokenizer in = new StreamTokenizer(new BufferedReader(
			new InputStreamReader(System.in)));
	int nextInt() throws IOException {
		in.nextToken();
		return (int) in.nval;
	}
	Union set[] = new Union[1010];
	void run() throws IOException {
		n = nextInt();
		int m = nextInt();
		for (int i = 1; i <= n; i++)
			set[i] = new Union(n);
		while (m-- > 0) {
			minx = nextInt();
			miny = nextInt();
			maxx = nextInt();
			maxy = nextInt();
			int res = 0;
			for (int i = minx; i <= maxx; i++)
				for (int j = miny; j <= maxy;j = set[i].find(j)+1)
					if (!vis[i][j]) {
						bfs(i, j);
						res++;
					}
			System.out.println(res);
		}
	}
	public static void main(String[] args) throws IOException {
		new CutingCake2843().run();

	}

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值