HNOI2006(BZOJ1191~1197)題解

06年的題目還是比較簡單的……爲防止查一道題時其他題被劇透而將題解部分調成白色了……



Day 1
超級英雄
將題目和錦囊分別視爲二分圖中兩部分的點,一個題目向能用的錦囊連邊,一邊加邊一邊匹配當前題目,直到不能匹配的位置就是答案。由於每題只連出兩條邊,所以單次匹配可以優化到O(1)。不過裸的匈牙利也能過。


鬼谷子的錢袋
答案即log(n)取上整。


馬步距離
這題其實有規律。如果對棋盤進行黑白染色,顯然走奇數步只能走到與起點異色的點,偶數步只能走到同色點。假設走x步,那麼終點到起點的曼哈頓距離應<=3x,且起點與終點的橫座標之差與縱座標之差都應<=2x。那麼一路枚舉下去即可。注意特判走三步和走四步的一些特殊情況。


潘多拉的盒子
這題應該是最神的一道題,據說咒語機就是一個自動機……首先判斷一個咒語機A是不是另一個B的升級有一種方法:
用二元組(x,y)表示當前狀態處在A的x點與B的y點上,初始爲(0,0)。轉移時x和y一起走到加0和加1後的位置,即(px0,py0)和(px1,py1),如果已經訪問過該狀態就不轉移。假設在某個狀態中x爲輸出元而y不是,那麼B就不能輸出這條A能輸出的咒語,即B不是A的升級。如果不存在這種狀態,B就是A的升級。
用上面的方法對每兩個咒語機都進行判斷然後建圖。建完之後縮強連通分量,得到的會是一個拓撲圖。在這個拓撲圖上DP即可。





Day 2
最短母串
最短的母串一定是將所有字串按一定順序重疊排列。那麼預處理出每兩個串能重疊的最長長度,開始DP。記f[i][j]表示最短長度,其中i是二進制數,表示已經選擇了哪些串,j是最後一個加入的串。轉移時枚舉每個串加入。答案即min{f[2^n-1][j]}。至於字典序最小,我的做法是保存每個f[i][j]對應的母串,轉移時同時判斷字典序。用字符數組幾乎是卡着內存過的……用string會好一些,不過會慢一點……


公路修建問題
二分答案,用類似Kruskal的方法判斷能夠加入多少條一級公路,如果不少於k條再判斷能加入的二級公路能否構成生成樹。


花仙子的魔法
好像變成經典DP問題了……觀察一維的情況:每加入一條線段最多增加兩個區間。然後考慮二維的情況:已有兩個相交的圓,現在加入第三個,可以發現如果把第三個圓展開成一條線段,其與其他兩個圓相交的位置也對應到線段上,那麼可以轉化成一維兩條線段的情況。由此得出DP:記f[i][j]爲最多的區間數,其中i爲維數,j爲施法次數。方程:f[i][j]=f[i][j-1] + f[i-1][j-1]。


軍機調度

有誰看懂了題求講解……





代碼:

超級英雄:

//BZOJ1191; Hero (HNOI2006); Bipartite Graph
#include <cstdio>
#include <cstdlib>
#define N 1000
#define M 1000

struct edge
{
	int next, node;
}e[M << 1 | 1];
int n, m, x, y, head[N + 1], tot = 0, match[N + 1], ans = 0;
bool v[N + 1];

inline void addedge(int a, int b)
{
	e[++tot].next = head[a];
	head[a] = tot, e[tot].node = b;
}

bool find(int x)
{
	for (int i = head[x]; i; i = e[i].next)
	{
		int node = e[i].node;
		if (v[node]) continue;
		v[node] = true;
		if (!match[node] || find(match[node]))
		{
			match[node] = x;
			return true;
		}
	}
	return false;
}

int main()
{
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d", &x, &y);
		++x, ++y;
		addedge(i, x), addedge(i, y);
	}
	for (int i = 1; !ans && i <= m; ++i)
	{
		for (int j = 1; j <= n; ++j) v[j] = false;
		if (!find(i)) ans = i;
	}
	ans = !ans ? m : ans - 1;
	printf("%d\n", ans);
	return 0;
}

鬼谷子的錢袋:

//BZOJ1192; 鬼谷子的錢袋 (HNOI2006);
#include <cstdio>
#include <cstdlib>

typedef long long ll;
ll n, x = 1LL;
int ans = 0;

int main()
{
	scanf("%lld", &n);
	while (x < n) x <<= 1LL, ++ans;
	printf("%d\n", ans);
}

馬步距離:

//BZOJ1193; 馬步距離; Observation
#include <cstdio>
#include <cstdlib>

int x1, x2, y1_, y2, d, ans;

int abs(int x)
{ return x < 0 ? -x : x; }

int main()
{
	scanf("%d%d%d%d", &x1, &y1_, &x2, &y2);
	d = abs(x1 - x2) + abs(y1_ - y2);
	for (ans = 0; ; ++ans)
	{
		if (((x1 + y1_ & 1) ^ (x2 + y2 & 1)) != (ans & 1)) continue;
		if (d <= ans * 3 && abs(x1 - x2) <= ans * 2 && abs(y1_ - y2) <= ans * 2) break;
	}
	if (d == 1) ans = 3;
	if (d == 4 && abs(x1 - x2) == 2 && abs(y1_ - y2) == 2)
		ans = 4;
	printf("%d\n", ans);
	return 0;
}

潘多拉的盒子:

//BZOJ1194; Pandora (HNOI2006);
#include <cstdio>
#include <cstdlib>
#include <utility>
#include <algorithm>
#define S 50
#define N 50
#define INFI 12345678

typedef std::pair<int, int> pair;
#define pair(x, y) std::make_pair(x, y)
int s, n[N + 1], m[N + 1], p[S + 1][N + 1][2], x, y, ans = 0;
int cnt[N + 1], f[N + 1], ind[N + 1], v[N + 1][N + 1], ct = 0, Q[N + 1], h, t;
bool con[N + 1][N + 1], out[N + 1][N + 1];
pair cur, q[N * N + 1];

inline bool check(int a, int b)
{
	++ct;
	h = t = 0;
	q[t++] = pair(0, 0);
	while (h < t)
	{
		cur = q[h++];
		if (out[a][cur.first] && !out[b][cur.second]) return false;
		x = p[a][cur.first][0], y = p[b][cur.second][0];
		if (v[x][y] != ct) v[x][y] = ct, q[t++] = pair(x, y);
		x = p[a][cur.first][1], y = p[b][cur.second][1];
		if (v[x][y] != ct) v[x][y] = ct, q[t++] = pair(x, y);
	}
	return true;
}

int main()
{
	scanf("%d", &s);
	for (int i = 1; i <= s; ++i)
	{
		scanf("%d%d", n + i, m + i);
		for (int j = 1; j <= m[i]; ++j)
		{
			scanf("%d", &x);
			out[i][x] = true;
		}
		for (int j = 0; j < n[i]; ++j)
			scanf("%d%d", &p[i][j][0], &p[i][j][1]);
	}
	
	for (int i = 1; i <= s; ++i)
		for (int j = 1; j <= s; ++j)
			if (i != j && check(i, j))
				con[i][j] = 1;
	
	for (int i = 1; i <= s; ++i) cnt[i] = 1;
	for (int i = 1; i <= s; ++i)
		for (int j = 1; j < i; ++j)
			if (cnt[j] && con[i][j] && con[j][i])
			{
				++cnt[j], cnt[i] = 0;
				break;
			}
	for (int i = 1; i <= s; ++i)
		for (int j = 1; j <= s; ++j)
			if (cnt[i] && cnt[j] && con[i][j]) ++ind[j];
	for (int i = 1; i <= s; ++i) f[i] = cnt[i];
	
	h = t = 0;
	for (int i = 1; i <= s; ++i)
		if (!ind[i] && cnt[i]) Q[t++] = i;
	while (h < t)
	{
		int cur = Q[h++];
		for (int i = 1; i <= s; ++i)
			if (con[cur][i] && cnt[i] && ind[i])
			{
				--ind[i];
				f[i] = std::max(f[i], f[cur] + cnt[i]);
				if (!ind[i]) Q[t++] = i;
			}
	}
	
	for (int i = 1; i <= s; ++i)
		ans = std::max(ans, f[i]);
	printf("%d\n", ans);
	return 0;
}

最短母串:

//BZOJ1195; 最短母串 (HNOI2006); State Compression DP
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <climits>
#include <algorithm>
#include <utility>
#include <queue>
#define N 12
#define LEN 50

typedef std::pair<int, int> pair;
#define pair(x, y) std::make_pair(x, y)
int n, l[N + 1], f[N + 1][1 << N], d[N + 1][N + 1], x, y, from[N + 1][1 << N];
int ans = INT_MAX, tq[N + 1], top;
char s[N + 1][LEN + 1], ts[N + 1][LEN * N + 1], str[N + 1][1 << N][LEN * N + 1], tmp[LEN * N + 1];
bool inq[N + 1][1 << N];
std::queue<pair> q;
pair cur;

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) scanf("%s", s[i]);
	for (int i = 1; i < n; ++i)
		for (int j = i + 1; j <= n; ++j)
			if (strcmp(s[i], s[j]) > 0)
			{
				strcpy(s[0], s[i]);
				memset(s[i], 0, sizeof(char) * l[i]);
				strcpy(s[i], s[j]);
				memset(s[j], 0, sizeof(char) * l[j]);
				strcpy(s[j], s[0]);
			}
	for (int i = 1; i <= n; ++i) l[i] = (int)strlen(s[i]);
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
		{
			if (i == j) continue;
			d[i][j] = l[i];
			for (int k = std::max(0, l[i] - l[j]); k < l[i]; ++k)
				if (!strncmp(s[i] + k, s[j], std::min(l[i] - k, l[j])))
				{
					d[i][j] = k;
					break;
				}
		}
	for (int i = 1; i <= n; ++i)
		f[i][1 << i - 1] = 0, q.push(pair(i, 1 << i - 1)), inq[i][1 << i - 1] = true;
	while (!q.empty())
	{
		y = q.front().first, x = q.front().second;
		inq[y][x] = false;
		q.pop();
		for (int i = 1; i <= n; ++i)
			if (!(x & (1 << i - 1)))
			{
				int nx = x | (1 << i - 1);
				if (!f[i][nx] || f[i][nx] > f[y][x] + d[y][i])
				{
					f[i][nx] = f[y][x] + d[y][i];
					from[i][nx] = y;
					strcpy(str[i][nx], str[y][x]);
					strncpy(str[i][nx] + f[y][x], s[y], d[y][i]);
					if (!inq[i][nx])
						q.push(pair(i, nx)), inq[i][nx] = true;
				}
				else if (f[i][nx] == f[y][x] + d[y][i])
				{
					strcpy(tmp, str[y][x]);
					strncpy(tmp + f[y][x], s[y], d[y][i]);
					tmp[f[y][x] + d[y][i] + 1] = 0;
					if (strcmp(tmp, str[i][nx]) < 0)
						strcpy(str[i][nx], tmp);
				}
			}
	}
	for (int i = 1; i <= n; ++i)
		if (f[i][(1 << n) - 1] + l[i] < ans)
			ans = f[i][(1 << n) - 1] + l[i], top = 1, tq[0] = i;
		else if (f[i][(1 << n) - 1] + l[i] == ans)
			tq[top++] = i;

	for (int i = 0; i < top; ++i)
	{
		strcpy(ts[i], str[tq[i]][(1 << n) - 1]);
		strcpy(ts[i] + f[tq[i]][(1 << n) - 1], s[tq[i]]);
	}
	x = 0;
	for (int i = 1; i < top; ++i)
		if (strcmp(ts[x], ts[i]) > 0) x = i;
	printf("%s\n", ts[x]);
	return 0;
}

公路修建問題:

//BZOJ1196; road (HNOI2006); Kruskal + Dichtomization
#include <cstdio>
#include <cstdlib>
#define N 10000
#define M 20000
#define MAX 30000

struct inedge
{
	int a, b, w1, w2;
}ie[M + 1];
int n, m, f[N + 1], k;

int find(int x)
{ return x == f[x] ? x : f[x] = find(f[x]); }

inline bool check(int x)
{
	int cnt = 0;
	for (int i = 1; i <= n; ++i) f[i] = i;
	for (int i = 1; cnt < n && i <= m; ++i)
		if (ie[i].w1 <= x && find(ie[i].a) != find(ie[i].b))
			++cnt, f[find(ie[i].a)] = find(ie[i].b);
	if (cnt < k) return false;
	for (int i = 1; cnt < n && i <= m; ++i)
		if (ie[i].w2 <= x && find(ie[i].a) != find(ie[i].b))
			++cnt, f[find(ie[i].a)] = find(ie[i].b);
	if (cnt >= n - 1) return true;
	return false;
}

int main()
{
	scanf("%d%d%d", &n, &k, &m);
	for (int i = 1; i <= m; ++i)
		scanf("%d%d%d%d", &ie[i].a, &ie[i].b, &ie[i].w1, &ie[i].w2);
	int l = 1, r = MAX;
	while (l < r)
	{
		int mid = l + r >> 1;
		if (check(mid)) r = mid;
		else l = mid + 1;
	}
	printf("%d\n", l);
	return 0;
}

花仙子的魔法:

//BZOJ1197; 花仙子的魔法 (HNOI2006); DP
#include <cstdio>
#include <cstdlib>
#define N 15
#define M 100

typedef long long ll;
int m, n;
ll f[N + 1][M + 1];

int main()
{
	scanf("%d%d", &m, &n);
	for (int i = 1; i <= m; ++i) f[1][i] = 2 * i;
	for (int i = 1; i <= n; ++i) f[i][1] = 2;
	for (int i = 2; i <= n; ++i)
		for (int j = 2; j <= m; ++j)
			f[i][j] = f[i - 1][j - 1] + f[i][j - 1];
	printf("%lld\n", f[n][m]);
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章