NOIP2014解題報告


day 1

1.生活大爆炸版石頭剪刀布(rps)

直接按照題意模擬即可

#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int maxn = 209;
const int Mark[5][5] = {{0, 0, 1, 1, 0}, {1, 0, 0, 1, 0}, {0, 1, 0, 0, 1}, {0, 0, 1, 0, 1}, {1, 1, 0, 0, 0}};

int N, Na, Nb;
int a[maxn], b[maxn];

int main() {
	
	scanf("%d%d%d", &N, &Na, &Nb);
	for(int i = 0; i < Na; i++) scanf("%d", a + i);
	for(int i = 0; i < Nb; i++) scanf("%d", b + i);
	
	int ansa = 0, ansb = 0, pa = 0, pb = 0;
	while(N--) {
		ansa += Mark[a[pa]][b[pb]];
		ansb += Mark[b[pb]][a[pa]];
		if((++pa) >= Na) pa -= Na;
		if((++pb) >= Nb) pb -= Nb;
	}
	printf("%d %d\n", ansa, ansb);
	
	return 0;
}

  

2.聯合權值(link)

比較基礎的樹形dp. mxx,cntx表示以x為根的子樹中結點x的孩子的最大權值和總權值, 在對樹進行dfs時計算出結果.

時間復雜度O(N)

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cctype>

using namespace std;

const int maxn = 200009;
const int MOD = 10007;

int N, w[maxn], mx[maxn], cnt[maxn], ans = 0, tot = 0;

inline int read() {
	char c = getchar();
	int ret = 0;
	for(; !isdigit(c); c = getchar());
	for(; isdigit(c); c = getchar())
	    ret = ret * 10 + c - '0';
	return ret;
}

struct edge {
	int to;
	edge* next;
} E[maxn << 1], *pt = E, *head[maxn];

inline void add(int u, int v) {
	pt->to = v; pt->next = head[u]; head[u] = pt++;
}
inline void addedge(int u, int v) {
	add(u, v); add(v, u);
}

void init() {
	scanf("%d", &N);
	for(int i = 1; i < N; i++) {
		int u = read() - 1, v = read() - 1;
		addedge(u, v);
	}
	for(int i = 0; i < N; i++) scanf("%d", w + i);
}

void dfs(int x, int fa = -1) {
	mx[x] = cnt[x] = 0;
	for(edge* e = head[x]; e; e = e->next) if(e->to != fa) {
		dfs(e->to, x);
		ans = max(ans, w[x] * mx[e->to]);
		ans = max(ans, w[e->to] * mx[x]);
		tot = (tot + w[x] * cnt[e->to] + w[e->to] * cnt[x]) % MOD;
		(cnt[x] += w[e->to]) %= MOD;
		mx[x] = max(mx[x], w[e->to]);
	}
}

int main() {
	
	init();
	dfs(0);
	printf("%d %d\n", ans, tot * 2 % MOD);
	
	return 0;
}

3.飛揚的小鳥(bird)

完全背包, dp(x, y)表示小鳥在坐標(x, y)處的最小點擊屏幕數.

從左到右dp, 狀態轉移方程為 dp(x, y) = min{dp(x-1,y-INCx-1)+1,dp(x,y-INCx-1)+1,dp(x,y+DECx-1)}

其中INCx表示橫坐標為x點擊一次屏幕的上升高度.

我們知道完全背包是要順序枚舉, 這道題也是, y要從1開始枚舉. 還要注意先算點擊屏幕的, 再算不點擊屏幕讓bird自己下來的, 這樣才不會重復. 不能到達終點就在dp過程中算一算.

時間復雜度O(NM)

#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int maxn = 10009;
const int inf = 0x3f3f3f3f;

int N, V, n;
int Inc[maxn], Dec[maxn], L[maxn], R[maxn], dp[2][maxn];

void init() {
	scanf("%d%d%d", &N, &V, &n);
	for(int i = 0; i < N; i++) 
		scanf("%d%d", Inc + i, Dec + i);
	for(int i = 1; i <= N; i++) L[i] = 0, R[i] = V + 1;
	while(n--) {
		int p; scanf("%d", &p);
		scanf("%d%d", L + p, R + p);
	}
}

int main() {
	
	init();
	int c = 0, p = 1, cnt = 0;
	memset(dp, inf, sizeof dp);
	for(int i = 1; i <= V; i++) dp[c][i] = 0;
	for(int i = 1; i <= N; i++) {
		swap(c, p);
		memset(dp[c], inf, sizeof dp[c]);
		for(int j = 1; j <= V; j++)if(j - Inc[i - 1] > 0)
			dp[c][j] = min(dp[c][j], min(dp[c][j - Inc[i - 1]], dp[p][j - Inc[i - 1]]) + 1);
		for(int j = V - Inc[i - 1]; j <= V; j++)
			dp[c][V] = min(dp[c][V], min(dp[p][j], dp[c][j]) + 1);
		for(int j = 1; j <= V; j++) if(j + Dec[i - 1] <= V)
			dp[c][j] = min(dp[c][j], dp[p][j + Dec[i - 1]]);
		for(int j = 1; j <= L[i]; j++) dp[c][j] = inf;
		for(int j = R[i]; j <= V; j++) dp[c][j] = inf;
		bool F = false;
		for(int j = L[i]; ++j < R[i]; )
			F |= dp[c][j] < inf;
		if(!F) {
			printf("0\n%d\n", cnt);
			return 0;
		}
		if(R[i] <= V) cnt++;
	}
	int ans = inf;
	for(int i = L[N]; ++i < R[N]; ) ans = min(ans, dp[c][i]);
	printf("1\n%d\n", ans);
	
	return 0;
}

day1的題目總體上比較簡單, 或者說有點過於簡單了.

 

day 2

1.無限網路發射器選址(wrieless)

直接枚舉每一個點, 再對於每一個點暴力算即可.

#include<cstdio>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn = 139;

int w[maxn][maxn];

int main() {
	
	memset(w, 0, sizeof w);
	int d, n;
	scanf("%d%d", &d, &n);
	while(n--) {
		int x, y; scanf("%d%d", &x, &y);
		scanf("%d", w[x] + y);
	}
	int tot, ans = 0;
	for(int i = 0; i < 129; i++)
		for(int j = 0; j < 129; j++) {
			int cnt = 0;
			for(int a = max(0, i - d); a <= min(128, i + d); a++)
				for(int b = max(0, j - d); b <= min(128, j + d); b++)
					cnt += w[a][b];
			if(cnt > ans)
				tot = 1, ans = cnt;
			else if(cnt == ans)
				tot++;
		}
	printf("%d %d\n", tot, ans);
	
	return 0;
}

2.尋找道路(road)

先從終點反向dfs一遍標出T能到達的點, 然后對於每個點檢查它的出邊是否被標記來確定是否可經過此點. 然后跑最短路即可.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>

using namespace std;

const int maxn = 10009;
const int inf = 0x3f3f3f3f;

struct edge {
	int to;
	edge* next;
} E[400009], *pt = E, *head[maxn];

void addedge(int u, int v) {
	pt->to = v; pt->next = head[u]; head[u] = pt++;
}


int N, S, T, d[maxn];
bool vis[maxn], ok[maxn], inq[maxn];
queue<int> q;

int sp() {
	if(!ok[S]) return -1;
	memset(d, inf, sizeof d);
	memset(inq, 0, sizeof inq);
	d[S] = 0; q.push(S); inq[S] = true;
	while(!q.empty()) {
		int x = q.front(); q.pop();
		for(edge* e = head[x]; e; e = e->next) if(ok[e->to] && d[e->to] > d[x] + 1) {
			d[e->to] = d[x] + 1;
			if(!inq[e->to])
				q.push(e->to), inq[e->to] = true;
		}
	}
	return d[T] != inf ? d[T] : -1;
}

namespace G {
	
	edge* head[maxn];
	
	void addedge(int u, int v) {
		pt->to = v; pt->next = head[u]; head[u] = pt++;
	}
	
	void dfs(int x) {
		if(vis[x]) return;
		vis[x] = true;
		for(edge* e = head[x]; e; e = e->next) dfs(e->to);
	}
}

int main() {
	
	memset(vis, 0, sizeof vis);
	int m;
	scanf("%d%d", &N, &m);
	while(m--) {
		int x, y; scanf("%d%d", &x, &y); x--; y--;
		if(x == y) continue;
		addedge(x, y);
		G::addedge(y, x);
	}
	scanf("%d%d", &S, &T); S--; T--;
	G::dfs(T);
	for(int i = 0; i < N; i++) {
		ok[i] = true;
		if(!vis[i]) {
			ok[i] = false;
			continue;
		}
		for(edge* e = head[i]; e; e = e->next)
			if(!vis[e->to]) ok[i] = false;
	}
	printf("%d\n", sp());
	
	return 0;
}

3.解方程(equation)

當f(x) = 0時, f(x) mod p = 0. 那么當f(x) mod p = 0時f(x)就有可能=0.

我們可以取幾個質數, 然后對於[1,m]每一個都進行驗證. 只需算出x在[1,p)中的結果即可, x在[p,m]上, f(x) mod p = f(x%p) mod p.

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>

using namespace std;

const int p[] = {20011, 20021, 20023, 20029, 20047};
const int maxn = 209;
const int pn = 5;

int a[pn][maxn], f[pn][23000], N, M;
int ans[1000009], n = 0;

void Read(int x) {
	bool F = true;
	char c = getchar();
	for(; !isdigit(c); c = getchar()) if(c == '-') F = false;
	int ret[pn];
	for(int i = 0; i < pn; i++) ret[i] = 0;
	for(; isdigit(c); c = getchar())
		for(int i = 0; i < pn; i++)
			ret[i] = (ret[i] * 10 + c - '0') % p[i];
	for(int i = 0; i < pn; i++)
		a[i][x] = F ? ret[i] : p[i] - ret[i];
}

int calculate(int x, int y) {
	int ret = 0;
	for(int i = N; i; i--)
		ret = (ret + a[x][i]) * y % p[x];
	if((ret += a[x][0]) >= p[x]) ret -= p[x];
	return ret;
}

int main() {
	
	scanf("%d%d", &N, &M);
	for(int i = 0; i <= N; i++) Read(i);
	for(int i = 0; i < pn; i++)
		for(int j = 0; j < p[i]; j++)
			f[i][j] = calculate(i, j);
	for(int i = 1; i <= M; i++) {
		bool t = true;
		for(int j = 0; j < pn; j++)
			if(f[j][i % p[j]]) t = false;
		if(t) ans[n++] = i;
	}
	printf("%d\n", n);
	for(int i = 0; i < n; i++) printf("%d\n", ans[i]);
	
	return 0;
}

day2前2題依舊是簡單, 第三題比較有難度


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM