POJ 1838 Banana【題解報告|爆搜不夠排序來湊】

題目鏈接

題目大意

有一隻猴子喜歡喫香蕉,但他只能在相鄰的兩棵香蕉樹之間行動,如果兩棵樹是在橫方向或豎方向相鄰的,那麼這就組成一個區域,這個區域內猴子可以隨意走動,有人可以把k個區域連接起來,從而使猴子在這k個區域內隨意行動,問猴子最多可以在多少棵樹內走動?給定n個樹的座標,和k。

BFS+MAP

使用MAP存儲已有點對,然後對輸入的N個點每個進行bfs,找到和他相鄰的所有點,並將已訪問過的點打上標記,這樣做雖然看起來複雜度不高,但是一般用map存儲點對很容易T,果不其然這裏就T了。

//TLE
#define inf 0x3f3f3f3f
#define ll long long
#define vec vector<int>
#define P pair<int,int>
#define MAX 16005

int N, K, X[MAX], Y[MAX];
map<P, bool>ma;
int dx[4] = { 0,0,1,-1 }, dy[4] = { 1,-1,0,0 };


int bfs(int sx, int sy) {
	int cnt = 1;
	queue<P> q; q.push(P(sx, sy)); ma[P(sx, sy)] = 0;
	while (!q.empty()) {
		int x = q.front().first, y = q.front().second; q.pop();
		for (int i = 0; i < 4; i++) {
			int xx = x + dx[i], yy = y + dy[i];
			if (xx <= 0 || xx > 10000 || yy <= 0 || yy > 10000)continue;
			//這是一顆還沒有訪問過的樹
			if (ma.find(P(xx, yy)) != ma.end() && ma[P(xx, yy)] == 1) {
				ma[P(xx, yy)] = 0;//訪問完他就不是樹了
				q.push(P(xx, yy));
				cnt++;
			}
		}
	}
	return cnt;
}

int main() {
	scanf("%d %d", &N, &K);
	ma.clear();
	for (int i = 0; i < N; i++)
		scanf("%d %d", &X[i], &Y[i]), ma[P(X[i], Y[i])] = 1;

	vec v;
	for (int i = 0; i < N; i++) {
		if (ma.find(P(X[i], Y[i])) != ma.end() && ma[P(X[i], Y[i])] == 1)
			v.push_back(bfs(X[i], Y[i]));
	}
	sort(v.begin(), v.end());

	int res = 0;
	for (int i = v.size() - 1; i >= v.size() - K && i >= 0; i--)
		res += v[i];
	cout << res << endl;
}

並查集

當題目中好像需要存儲點對時,如果map T了,我們不妨考慮一下一些優化的手段,常見的手段即按照橫,縱座標排序然後進行處理,這裏也可以使用這種方法。

因爲相鄰的banana一定是橫座標相等縱座標差1,或者縱座標相等橫座標差1,那麼我們將其分別按照橫縱座標排序,按x排序時統計y相鄰的點,將他們分成多個組,然後按y排序時將x相鄰的點所在的組合並起來。合併組別就用到了並查集這個高效的工具

//476K	204MS
#define inf 0x3f3f3f3f
#define ll long long
#define vec vector<int>
#define P pair<int,int>
#define MAX 16005

int N, K, kind[MAX], cnt[MAX];

struct p {
	int x, y, id;
}ps[MAX];
bool cmp(p p1, p p2) {
	if (p1.x == p2.x)return p1.y < p2.y;
	else return p1.x < p2.x;
}
bool ccmp(p p1, p p2) {
	if (p1.y == p2.y)return p1.x < p2.x;
	else return p1.y < p2.y;
}

int find(int k) {
	if (k == kind[k])return k;
	else return kind[k] = find(kind[k]);
}
void unite(int a, int b) { 
	int p1 = find(b), p2 = find(a);
	if (p1 == p2)return;//同一集合,無需合併
	kind[p1] = p2; cnt[p2] += cnt[p1]; cnt[p1] = 0;
}

int main() {
	scanf("%d %d", &N, &K);
	for (int i = 1; i <= N; i++)
		scanf("%d %d", &ps[i].x, &ps[i].y), ps[i].id = i;
	for (int i = 1; i <= N; i++)kind[i] = i, cnt[i] = 1;
	
	int i = 0, j = 0, res = 0;
	//按照x排序,找到x相同y相差爲1的點
	sort(ps + 1, ps + 1 + N, cmp);
	for (i = 2; i <= N; i++) {
		if (ps[i].x == ps[i - 1].x&&ps[i].y == ps[i - 1].y + 1)
			unite(ps[i - 1].id, ps[i].id);
	}

	//按照y排序,找到y相同x相差爲1的點
	sort(ps + 1, ps + 1 + N, ccmp);
	for (i = 2; i <= N; i++) {
		if (ps[i].y == ps[i - 1].y&&ps[i].x == ps[i - 1].x + 1)
			unite(ps[i - 1].id, ps[i].id);
	}

	sort(cnt + 1, cnt + N + 1);
	for (int i = N; i > N - K; i--)
		res += cnt[i];
	cout << res << endl;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章