zoukankan      html  css  js  c++  java
  • 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
    ", 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
    ", 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
    %d
    ", 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
    %d
    ", 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
    ", 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
    ", 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);
    	for(int i = 0; i < n; i++) printf("%d
    ", ans[i]);
    	
    	return 0;
    }
    

    day2前2题依旧是简单, 第三题比较有难度

  • 相关阅读:
    jaxb解析xml工具类
    JQuery的父、子、兄弟节点查找方法
    jw player 配置参数
    jQuery判断当前元素是第几个元素&获取第N个元素
    正则表达式中test,match,exec区别
    php常用函数file
    php常用函数time
    php常用array函数
    php常用string函数
    Linux常用命令
  • 原文地址:https://www.cnblogs.com/JSZX11556/p/4907703.html
Copyright © 2011-2022 走看看