Archive

Posts Tagged ‘HDU’

HDU Farm Irrigation 1198

April 9th, 2010 No comments

简单深搜,注意如何判断两个方块是否相通

#include <iostream>
using namespace std;
#define TOLEFT(T) ((T) == 'A' || (T) == 'C' || (T) == 'F' || (T) == 'G' || (T) == 'H' || (T) == 'I' || (T) == 'K')
#define TORIGHT(T) ((T) == 'B' || (T) == 'D' || (T) == 'F' || (T) == 'G' || (T) == 'I' || (T) == 'J' || (T) == 'K')
#define TOUP(T) ((T) == 'A' || (T) == 'B' || (T) == 'E' || (T) == 'G' || (T) == 'H' || (T) == 'J' || (T) == 'K')
#define TODOWN(T) ((T) == 'C' || (T) == 'D' || (T) == 'E' || (T) == 'H' || (T) == 'I' || (T) == 'J' || (T) == 'K')
int h, w;
char mp[60][60];
int v[60][60];
const int dh[] = {-1, 0, 1, 0};
const int dw[] = {0, 1, 0, -1};
const int ZONG = 0;
const int HENG = 1;
bool isConnect(int h1, int w1, int h2, int w2, int dir) {
    if(dir == HENG) {
        if(w1 > w2) {
            swap(h1, h2);
            swap(w1, w2);
        }
        return TORIGHT(mp[h1][w1]) && TOLEFT(mp[h2][w2]);
    } else {
        if(h1 > h2) {
            swap(h1, h2);
            swap(w1, w2);
        }
        return TODOWN(mp[h1][w1]) && TOUP(mp[h2][w2]);
    }
}
void dfs(int i, int j) {
    v[i][j] = 1;
    int th, tw;
    for(int k = 0; k < 4; ++k) {
        th = i + dh[k];
        tw = j + dw[k];
        if(th < 0 || th >= h || tw < 0 || tw >= w || v[th][tw] == 1 || !isConnect(i, j, th, tw, ((k&1)?HENG:ZONG))) continue;
        dfs(th, tw);
    }
}
int main() {
    int ans;
    while(scanf("%d%d", &h, &w)) {
        if(h < 0 || w < 0) break;
        ans = 0;
        for(int i = 0; i < h; ++i) scanf("%s", mp[i]);
        memset(v, 0, sizeof(v));
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j) if(v[i][j] == 0) {
                ++ans;
                dfs(i, j);
            }
        printf("%d\n", ans);
    }
}
Categories: ACM Tags: , ,

HDU 连连看 1175

April 6th, 2010 No comments

题目地址
注意该题不能从外面绕
判断两点是否可以消去
消去可能是可以直接一条横线连,也可能是一个折的线或者是二个折的线
我用的方法,可以同时全部考虑进去
假设A,B是两个要判断是否消去的点,如果两点数字不同,或者都为0,当然不能消去
否则计算出A,B两点向四个方向能延伸到多远

1 2 3 4
0 0 0 0
4 3 2 1
为例
如果A=(1,1),B=(3,4)(坐标从1开始)
那么A按上右下左的顺序能延伸的范围是1, 1, 2, 1
B按上右下左的顺序能延伸的范围是2, 4, 3, 4
即向上下的话,是记录其纵坐标的范围,左右的话是记录横坐标的范围
现在先来判断上下是否连接,
那么先得出A,B两者左右延伸的公共范围,易知A左右延伸为(1,1),B为(4,4)故其没有公共范围
再来判断左右是否连接
那么先得出A,B两者上下延伸的公共范围,易知A上下延伸为(1,2),B为(2,3)故其公共范围为(2,2)
遍历纵坐标为(2,2)中的所有数且横坐标为A的横坐标的点,看每个点左右延伸范围是否有包括B点,如果有则可以消去
注意到两点刚好邻接的情况,应该将延伸范围左右扩大1
例如
1 1
这个明显可以消去,但如果按延伸范围是否包括的话,则为不能消去,所以必须加1,注意到代码注释的地方

#include <iostream>
using namespace std;
int a[1001][1001], h, w;
const int dh[] = {-1, 0, 1, 0};
const int dw[] = {0, 1, 0, -1};
struct Point {
    int h, w;
    int dir[4];
} st, en;
void cal(Point v, int *p) {
    Point tmp;
    for(int i = 0; i < 4; ++i) {
        tmp = v;
        do {
            tmp.h += dh[i];
            tmp.w += dw[i];
        } while(tmp.h >= 0 && tmp.h < h && tmp.w >= 0 && tmp.w < w && a[tmp.h][tmp.w] == 0);
        tmp.h -= dh[i];
        tmp.w -= dw[i];
        p[i] = ((i&1) ? tmp.w : tmp.h);
    }
}
void cal1(Point v, int *p, int i) {
    Point tmp;
    tmp = v;
    do {
        tmp.h += dh[i];
        tmp.w += dw[i];
    } while(tmp.h >= 0 && tmp.h < h && tmp.w >= 0 && tmp.w < w && a[tmp.h][tmp.w] == 0);
    tmp.h -= dh[i];
    tmp.w -= dw[i];
    p[i] = ((i&1) ? tmp.w : tmp.h);
}
bool solve() {
    if(a[st.h][st.w] == 0 || a[st.h][st.w] != a[en.h][en.w]) return false;
    int mmin, mmax;
    cal(st, st.dir);
    cal(en, en.dir);
    Point a1 = st, a2 = en;
    if(a1.w > a2.w) swap(a1, a2);
    mmin = max(a1.dir[0], a2.dir[0]);
    mmax = min(a1.dir[2], a2.dir[2]);
    Point tmp;
    tmp.w = a1.w;
    for(int i = mmin; i <= mmax; ++i) {
        tmp.h = i;
        cal1(tmp, tmp.dir, 1);
        //这里必须加1,理由看开头注释
        if(tmp.dir[1] + 1 >= a2.w) return true;
    }
    if(a1.h > a2.h) swap(a1, a2);
    mmin = max(a1.dir[3], a2.dir[3]);
    mmax = min(a1.dir[1], a2.dir[1]);
    tmp.h = a1.h;
    for(int i = mmin; i <= mmax; ++i) {
        tmp.w = i;
        cal1(tmp, tmp.dir, 2);
        //这里必须加1,理由看开头注释
        if(tmp.dir[2] + 1 >= a2.h) return true;
    }
    return false;
}
int main() {
    int p;
    while(scanf("%d%d", &h, &w)) {
        if(h == 0 && w == 0) break;
        for(int i = 0; i < h; ++i)
            for(int j = 0; j < w; ++j)
                scanf("%d", &a[i][j]);
        scanf("%d", &p);
        while(p--) {
            scanf("%d%d%d%d", &st.h, &st.w, &en.h, &en.w);
            st.h -= 1, st.w -= 1, en.h -= 1, en.w -= 1;
            solve() ? puts("YES") : puts("NO");
        }
    }
    return 0;
}
Categories: ACM Tags: ,

HDU Picture 1828

May 6th, 2009 No comments

题目地址

给定一些矩形,求这些矩形围成的周长。(具体看题目的图)
很囧很囧,弄了我好多时间,,跟求矩形并的方法类似,但是加了判断结点覆盖的情况。
一开始,没有去掉y里重复的数值,而我在update时

 
if(nod[t].lf == e.y1 && nod[t].rf == e.y2) nod[t].cnt += e.f;
else {
	if(e.y2 <= nod[t*2].rf) update(t*2, e);
	else if(e.y1 >= nod[t*2+1].lf) update(t*2+1, e);
	else {
		Line tmp = e;
		tmp.y2 = nod[t*2].rf, update(t*2, tmp);
		tmp = e, tmp.y1 = nod[t*2+1].lf, update(t*2+1, tmp);
	}
}

 这种方法对于区间两端如果数值一样的话,是无法update到那个区间的,一个比较通用的方法是

 if(e.y1 <= nod[t].lf && nod[t].rf <= e.y2) nod[t].cnt += e.f;
else if(nod[t].l + 1 != nod[t].r) {
	int m = (nod[t].l+nod[t].r) / 2;
	if(e.y1 < y[m]) update(t*2, e);
	if(y[m] < e.y2) update(t*2+1, e);
}

 这样即使数值一样,也可以。突然想到,貌似之前几道矩形并交的题都是使用上面的那种方法,貌似都没有出问题,其实是因为求矩形面积并时如果有端点一样的情况,访问到那里其数值也是为0,因为长度为0,而对于这题,由于需要判断端点是否重复覆盖,故一定要访问到,在端点有一样的情况下,第一种方法就不适用了。
不过最后我还是通过把y去重,这样结点数会变少许多,orz…
Meomry: 492K
Time: 15MS

#include <iostream>
#include <algorithm>
#include <math.h>
using namespace std;
const int maxn = 10001;
struct {
	int l, r, lf, rf, ld, rd, cnt, len, lines;
}nod[maxn*3];
struct Line {
	int x, y1, y2, f;
	bool operator < (const Line &a) const {
		return x < a.x || x == a.x && f > a.f;
	}
}line[maxn];
int y[maxn];
void creat(int t, int l, int r) {
	nod[t].cnt = nod[t].len = nod[t].ld = nod[t].rd = nod[t].lines = 0;
	nod[t].l = l, nod[t].r = r, nod[t].lf = y[l], nod[t].rf = y[r];
	if(l + 1 == r) return;
	int m = (l+r) >> 1;
	creat(t*2, l, m), creat(t*2+1, m, r);
}
void calen(int t) {
	nod[t].len = 0;
	if(nod[t].cnt > 0) nod[t].len = nod[t].rf - nod[t].lf;
	else if(nod[t].l + 1 != nod[t].r) nod[t].len = nod[t*2].len + nod[t*2+1].len;
}
void caline(int t) {
	nod[t].ld = nod[t].rd = nod[t].lines = 0;
	if(nod[t].cnt > 0) nod[t].ld = nod[t].rd = nod[t].lines = 1;
	else if(nod[t].l + 1 != nod[t].r) nod[t].ld = nod[t*2].ld, nod[t].rd = nod[t*2+1].rd, nod[t].lines = nod[t*2].lines + nod[t*2+1].lines - (nod[t*2].rd & nod[t*2+1].ld);
}
void update(int t, Line e) {
	if(nod[t].lf == e.y1 && nod[t].rf == e.y2) nod[t].cnt += e.f;
	else {
		if(e.y2 <= nod[t*2].rf) update(t*2, e);
		else if(e.y1 >= nod[t*2+1].lf) update(t*2+1, e);
		else {
			Line tmp = e;
			tmp.y2 = nod[t*2].rf, update(t*2, tmp);
			tmp = e, tmp.y1 = nod[t*2+1].lf, update(t*2+1, tmp);
		}
	}
	//if(e.y1 <= nod[t].lf && nod[t].rf <= e.y2) nod[t].cnt += e.f;
	//else if(nod[t].l + 1 != nod[t].r) {
	//	int m = (nod[t].l+nod[t].r) / 2;
	//	if(e.y1 < y[m]) update(t*2, e);
	//	if(y[m] < e.y2) update(t*2+1, e);
	//}
	calen(t), caline(t);
}
int main() {
	int n, x1, y1, x2, y2, i, t, s, preline, prelen, l;
	while(scanf("%d", &n) != EOF) {
		for(i = 0, t = 1; i < n; i++) {
			scanf("%d%d%d%d", &x1, &y1, &x2, &y2);
			line[t].x = x1, line[t+1].x = x2;
			line[t].y1 = line[t+1].y1 = y[t] = y1;
			line[t].y2 = line[t+1].y2 = y[t+1] = y2;
			line[t].f = 1, line[t+1].f = -1;
			t += 2;
		}
		sort(line+1, line+t), sort(y+1, y+t);
		for(i = 2, l = 2; i < t; i++) if(y[i] != y[i-1]) y[l++] = y[i];
		creat(1, 1, l-1);
		s = preline = prelen = 0;
		for(i = 1; i < t; i++) {
			update(1, line[i]);
			s += abs(nod[1].len-prelen) + 2 * preline * (line[i].x - line[i-1].x);
          	prelen = nod[1].len, preline = nod[1].lines;
		}
		printf("%d\n", s);
	}
	return 0;
}
Categories: ACM Tags: ,

HDU 覆盖的面积 1255

April 30th, 2009 No comments

题目地址
题目要求覆盖大于等于二次以上的面积,其实就是求矩形面积的交集。
想法跟1542的差不多,加了个重复测度
Memory: 492K
Time: 343MS

#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 2001
struct {
	int c, l, r;
	double cnt, recnt, lf, rf;
}nod[MAXN*3];
struct Line {
	double x, y1, y2;
	int f;
}line[MAXN];
bool cmp(Line a, Line b) {
	return a.x < b.x;
}
double y[MAXN];
void creat(int t, int l, int r) {
	nod[t].c = nod[t].cnt = nod[t].recnt = 0;
	nod[t].l = l, nod[t].r = r;
	nod[t].lf = y[l], nod[t].rf = y[r];
	if(l + 1 == r) return;
	int m = (l+r) / 2;
	creat(t*2, l, m), creat(t*2+1, m, r);
}
void calen(int t) {
	if(nod[t].c > 0) {
		nod[t].cnt = nod[t].rf - nod[t].lf;
		return;
	}
	if(nod[t].l + 1 == nod[t].r) nod[t].cnt = 0;
	else nod[t].cnt = nod[t*2].cnt + nod[t*2+1].cnt;
}
void carelen(int t) {
	if(nod[t].c > 1) {
		nod[t].recnt = nod[t].rf - nod[t].lf;
		return;
	}
	if(nod[t].l + 1 == nod[t].r) nod[t].recnt = 0;
	else {
		if(nod[t].c == 1) nod[t].recnt = nod[t*2].cnt + nod[t*2+1].cnt;
		else nod[t].recnt = nod[t*2].recnt + nod[t*2+1].recnt;
	}
}
void update(int t, Line e) {
	if(e.y1 == nod[t].lf && e.y2 == nod[t].rf) {
		nod[t].c += e.f;
		calen(t);
		carelen(t);
		return;
	}
	if(e.y2 <= nod[t*2].rf) update(t*2, e);
	else if(e.y1 >= nod[t*2+1].lf) update(t*2+1, e);
	else {
		Line tmp = e;
		tmp.y2 = nod[t*2].rf;
		update(t*2, tmp);
		tmp = e;
		tmp.y1 = nod[t*2+1].lf;
		update(t*2+1, tmp);
	}
	calen(t);
	carelen(t);
}
int main() {
	int n, i, t, cases;
	double x1, x2, y1, y2, ans;
	scanf("%d", &cases);
	while(cases--) {
		scanf("%d", &n);
		t = 1;
		ans = 0;
		for(i = 1; i <= n; i++) {
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			line[t].x = x1, line[t+1].x = x2;
			line[t].y1 = line[t+1].y1 = y[t] = y1, line[t].y2 = line[t+1].y2 = y[t+1] = y2;
			line[t].f = 1, line[t+1].f = -1;
			t += 2;
		}
		sort(line+1, line+t, cmp), sort(y+1, y+t);
		creat(1, 1, t-1);
		update(1, line[1]);
		for(i = 2; i < t; i++) {
			ans += nod[1].recnt * (line[i].x - line[i-1].x);
			update(1, line[i]);
		}
		printf("%.2lf\n", ans);
	}
	return 0;
}
Categories: ACM Tags: ,

HDU Atlantis 1542

April 30th, 2009 No comments

题目地址
题目意思很简单,要求计算被矩形覆盖过的面积,即求矩形面积的并集
对顶点进行离散化,对顶点的Y坐标从小到大排序,然后以这些点建立线段树。结点存储该区间被完全覆盖的次数c,该区间的测试cnt,以及对Y坐标的映射。对于每一个X坐标,如果是线段的起点,则覆盖次数加1,代表线段开始,如果是线段的终点,则减1,代表该线段覆盖的范围已经结束
Memory: 248K
Time: 0MS

#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 201
struct {
	int c, l, r;
	double cnt, lf, rf;
}nod[MAXN*3];
struct Line {
	double x, y1, y2;
	int f;
}line[MAXN];
bool cmp(Line a, Line b) {
	return a.x < b.x;
}
double y[MAXN];
void creat(int t, int l, int r) {
	nod[t].c = nod[t].cnt = 0;
	nod[t].l = l, nod[t].r = r;
	nod[t].lf = y[l], nod[t].rf = y[r];
	if(l + 1 == r) return;
	int m = (l+r) / 2;
	creat(t*2, l, m), creat(t*2+1, m, r);
}
void calen(int t) {
	if(nod[t].c > 0) {
		nod[t].cnt = nod[t].rf - nod[t].lf;
		return;
	}
	if(nod[t].l + 1 == nod[t].r) nod[t].cnt = 0;
	else nod[t].cnt = nod[t*2].cnt + nod[t*2+1].cnt;
}
void update(int t, Line e) {
	if(e.y1 == nod[t].lf && e.y2 == nod[t].rf) {
		nod[t].c += e.f;
		calen(t);
		return;
	}
	if(e.y2 <= nod[t*2].rf) update(t*2, e);
	else if(e.y1 >= nod[t*2+1].lf) update(t*2+1, e);
	else {
		Line tmp = e;
		tmp.y2 = nod[t*2].rf;
		update(t*2, tmp);
		tmp = e;
		tmp.y1 = nod[t*2+1].lf;
		update(t*2+1, tmp);
	}
	calen(t);
}
int main() {
	int n, i, t, cases = 1;
	double x1, x2, y1, y2, ans;
	while(scanf("%d", &n), n) {
		t = 1;
		ans = 0;
		for(i = 1; i <= n; i++) {
			scanf("%lf%lf%lf%lf", &x1, &y1, &x2, &y2);
			line[t].x = x1, line[t+1].x = x2;
			line[t].y1 = line[t+1].y1 = y[t] = y1, line[t].y2 = line[t+1].y2 = y[t+1] = y2;
			line[t].f = 1, line[t+1].f = -1;
			t += 2;
		}
		sort(line+1, line+t, cmp), sort(y+1, y+t);
		creat(1, 1, t-1);
		update(1, line[1]);
		for(i = 2; i < t; i++) {
			ans += nod[1].cnt * (line[i].x - line[i-1].x);
			update(1, line[i]);
		}
		printf("Test case #%d\nTotal explored area: %.2lf\n\n", cases++, ans);
	}
	return 0;
}
Categories: ACM Tags: ,

HDU I Hate It 1754

April 27th, 2009 No comments

题目地址
询问区间最值,并执行某个点的变更操作
没什么好说的,线段树的入门基础题。
Memory: 7148K
Time: 468MS

#include <iostream>
using namespace std;
inline int Max(int a, int b){return a > b ? a : b;}
const int MAXN = 200001;
struct {
	int l, r, m;
} nod[MAXN*3];
int a[MAXN];
void creat(int t, int l, int r) {
	nod[t].l = l, nod[t].r = r;
	if(l == r) {nod[t].m = a[l]; return;}
	int m = (l+r) / 2;
	creat(t*2, l, m), creat(t*2+1, m+1, r);
	nod[t].m = Max(nod[t*2].m, nod[t*2+1].m);
}
int query(int t, int l, int r) {
	if(l == nod[t].l && r == nod[t].r) return nod[t].m;
	int s;
	if(r <= nod[t*2].r) s = query(t*2, l, r);
	else if(l >= nod[t*2+1].l) s= query(t*2+1, l, r);
	else s = Max(query(t*2, l, nod[t*2].r), query(t*2+1, nod[t*2+1].l, r));
	return s;
}
void update(int t, int n, int v) {
	if(nod[t].l == nod[t].r && nod[t].l == n) {nod[t].m = v; return;}
	if(n <= nod[t*2].r) update(t*2, n, v);
	else update(t*2+1, n, v);
	nod[t].m = Max(nod[t*2].m, nod[t*2+1].m);
}
int main() {
	int n, m, i, x1, x2;
	char s[2];
	while(scanf("%d%d", &n, &m) != EOF) {
		for(i = 1; i <= n; i++) scanf("%d", &a[i]);
		creat(1, 1, n);
		while(m--) {
			scanf("%s%d%d", s, &x1, &x2);
			if(s[0] == 'Q') printf("%d\n", query(1, x1, x2));
			else update(1, x1, x2);
		}
	}
	return 0;
}
Categories: ACM Tags: ,

HDU Just a Hook 1698

April 27th, 2009 No comments

题目地址
给一组棍子染色,不同的颜色有不同的值,执行一系列的区间染色后,问这组棍子的总值是多少。
每个线段树的结点,存储该结点所代表区间的染色状况c,如果该区间全为同样的颜色,则用一个正数表示,如果含有多种颜色,则用-1表示,每次执行染色操作时,如果所要染的颜色与区间颜色一样,则停止,如果所要染区间跟当前结点区间一致,则直接将所要染颜色赋给当前结点的c,否则,如果当前结点区间不是混合色,则先将当前结点的颜色赋给左右子结点的c,并递归下去。
Memory: 3284k
Time: 406MS

#include <iostream>
using namespace std;
const int MAXN = 100001;
struct {
	int l, r, c;
} nod[MAXN*3];
void creat(int t, int l, int r) {
	nod[t].l = l, nod[t].r = r, nod[t].c = 1;
	if(l == r) return;
	int m = (l+r) / 2;
	creat(t*2, l, m), creat(t*2+1, m+1, r);
}
void color(int t, int l, int r, int c) {
	if(c == nod[t].c) return;
	if(l == nod[t].l && r == nod[t].r) {nod[t].c = c; return;}
	if(nod[t].c != -1) nod[t*2].c = nod[t*2+1].c = nod[t].c;
	nod[t].c = -1;
	if(r <= nod[t*2].r) color(t*2, l, r, c);
	else if(l >= nod[t*2+1].l) color(t*2+1, l, r, c);
	else color(t*2, l, nod[t*2].r, c), color(t*2+1, nod[t*2+1].l, r, c);
}
int count(int t, int l, int r) {
	if(nod[t].c > 0) return (r-l+1) * nod[t].c;
	return count(t*2, l, nod[t*2].r) + count(t*2+1, nod[t*2+1].l, r);
}
int main() {
	int cases, n, q, x1, x2, c, t = 1;
	scanf("%d", &cases);
	while(cases--) {
		scanf("%d%d", &n, &q);
		creat(1, 1, n);
		while(q--) {scanf("%d%d%d", &x1, &x2, &c); color(1, x1, x2, c);}
		int aa;
		printf("Case %d: The total value of the hook is %d.\n", t++, count(1, 1, n));
	}
	return 0;
}
Categories: ACM Tags: ,

HDU 搬寝室 1421

April 26th, 2009 No comments

题目地址

题目大意是,给定n个数,每次选择2个数(取出后不放回),求出两数之差的平方,累加,选k对(k<=n/2),问最终累加之和最小为多少

先对n个数排序,因为结果要求最小,那么每次择时,只能选择相邻的两个数,只有这样,最终的差才可能最小,构造dp[i][j],代表从1到n个物品中选择k对,那么状态转移方程为

dp[i][j]=Min{dp[i-2][j-1]+a[i-1],dp[i-1][j]}

其中a[i-1]存放的是第i与第i-1个数的差的平方。

#include <iostream>
#include <algorithm>
using namespace std;
#define Min(a,b) ((a) < (b) ? (a) : (b))
#define INF 2100000000
int dp[2001][1001];
int main() {
    int i, j, n, k, a[2001];
    while(scanf("%d%d", &n, &k) != EOF) {
        for(i = 1; i <= n; i++) scanf("%d", &a[i]);
        sort(a+1, a+1+n);
        for(i = 1; i < n; i++) a[i] = (a[i]-a[i+1]) * (a[i]-a[i+1]);
        for(i = 0; i <= n; i++)
            for(j = 1; j <= k; j++)
                dp[i][j] = INF;
        for(i = 0; i <= n; i++) dp[i][0] = 0;
        for(i = 2; i <= n; i++)
            for(j = 1; j * 2 <= i; j++)
                dp[i][j] = Min(dp[i-2][j-1]+a[i-1], dp[i-1][j]);
        printf("%d\n", dp[n][k]);
    }
    return 0;
}
Categories: ACM Tags: ,