[NHZXOI2017]【NOIP2016】总结

NOIP2016提高组复赛

题目连接:http://wenku.baidu.com/view/c58b2ffc9f3143323968011ca300a6c30d22f155.html?re=view

估计分值:270; 洛谷测试:190; 实际分值:85。

第一题:玩具谜题
考察知识点:循环队列 字符串操作
代码:
#include<iostream>
#include<string.h>
using namespace std;
int main(){
	freopen("toy.in","r",stdin);
	freopen("toy.out","w",stdout);
	int n,m,i,od,k,now,a[100010];
	cin >>n >>m;
	string s[100010];
	for (i=1; i<=n; i++){
		cin >>a[i] >>s[i];
		if (a[i]==0) a[i]=1; else a[i]=-1;
	}
	now=1;
	for (i=1; i<=m; i++){
		cin >>od >>k;
		if (od==0) od=-1;
		now+=k*a[now]*od;
		if (now<1) now+=n;
		if (now>n) now-=n;
	}
	for (i=0; i<s[now].size(); i++) cout <<s[now][i];
	fclose(stdin); fclose(stdout);
	return 0;
}

第二题:天天爱跑步
考查知识点:(有待补充)以我所知为树的知识
做法:水分,前四个点,可以直接判断;第五个点开始BFS
代码:
#include<iostream>
#include<vector>
#include<string.h>
using namespace std;
int n,m,u[10010],v[10010],w[10010],s[10010],t[10010],ans[10010],visit[10010];
vector <int> g[10010];
struct node{
	int v,father,lc,rc;
};
void point1(){
	int i;
	for (i=1; i<n; i++) cin >>u[i] >>v[i];
	for (i=1; i<=n; i++) cin >>w[i];
	for (i=1; i<=m; i++){
		cin >>s[i] >>t[i];
		if (w[s[i]]==0) ans[s[i]]++;
	}
	for (i=1; i<=n; i++) cout <<ans[i] <<' ';
	fclose(stdin); fclose(stdout);
}
void point2(){
	int i;
	for (i=1; i<n; i++) cin >>u[i] >>v[i];
	for (i=1; i<=n; i++) cin >>w[i];
	for (i=1; i<=m; i++){
		cin >>s[i] >>t[i];
		ans[s[i]]++;
	}
	for (i=1; i<=n; i++) cout <<ans[i] <<' ';
	fclose(stdin); fclose(stdout);
}

void bfs(int p,int num,int t){
	if (p==num){
		if (w[p]==0) ans[p]++;
		return;
	} 
	int q1[10010],q2[10010],time[10010],i,l,r,now;
	memset(visit,0,sizeof(visit));
	q1[1]=p; time[1]=0; l=1; r=1; q2[1]=0; visit[p]=1;
	while (l<=r){
		now=q1[l];
		for (i=0; i<g[now].size(); i++){
			if (visit[g[now][i]]==1) continue;
			r++; q1[r]=g[now][i]; time[r]=time[l]+1; q2[r]=l; visit[g[now][i]]=1;
			if (g[now][i]==num) {
				now=r;
				while (now>0){
					if (w[q1[now]]==time[now]) ans[q1[now]]++;
					now=q2[now];
				}
				return;
			}
		}
		l++;
	}
}

int main(){
	freopen("running.in","r",stdin);
	freopen("running.out","w",stdout);
	cin >>n >>m;
	if (n==991){
		point1(); exit;
	}
	if (n==992){
		point2(); exit;
	}
	int i;
	for (i=1; i<n; i++){
	  cin >>u[i] >>v[i];
	  g[u[i]].push_back(v[i]);
	  g[v[i]].push_back(u[i]);
	} 
	for (i=1; i<=n; i++) cin >>w[i];
	for (i=1; i<=m; i++){
		cin >>s[i] >>t[i];
		bfs(s[i],t[i],0);
	}
	for (i=1; i<=n; i++) cout <<ans[i] <<' ';
	fclose(stdin); fclose(stdout);
	return 0;
}

第三题:换教室
考查知识点:数学期望、DP、floyd
题解
floyd:把每个教室之间的最短路径求出来,即为图中的任意两点间的最短路径,又因为 1≤v≤300,所以用floyd方法,时间为O(n * n * n)
DP:题目中问当前的教室是否要申请,申请后又会使路径发生改变,所以就有一点选和不选,01背包的(动态规划)味道。其实,是可以的。令f[i][j]表示前i个教室,有j个教室申请了的最小值。这样还不够,还需要开多一维来记录当前的教室是否申请,才为后面计算指明方向。又因为这个期望值的计算只与上一个状态(概率)有关,易得转移方程:
f[i][j][0] = min(f[i - 1][j][0] + G[a[i - 1]][a[i]], f[i - 1][j][1] + G[b[i - 1]][a[i]] * p[i - 1] + G[a[i - 1]][a[i]] * (1 - p[i-1]));
f[i][j][1] = min(f[i - 1][j - 1][0] + G[a[i - 1]][b[i]] * p[i] + G[a[i - 1]][a[i]] * (1 - p[i]), 
 f[i - 1][j - 1][1] + G[b[i - 1]][b[i]] * p[i] * p[i - 1] + G[b[i - 1]][a[i]] * p[i - 1] * (1 - p[i]) 
                  + G[a[i - 1]][b[i]] * (1 - p[i - 1]) * p[i] + G[a[i - 1]][a[i]] * (1 - p[i - 1]) * (1 - p[i]));
注:G[i][j]表示i到j之间的最短距离,p[i]表示当前可是申请通过的概率。
代码:
#include<bits/stdc++.h>
using namespace std;
const int MAX_N = 2000 + 4;
int a[MAX_N],b[MAX_N],G[305][305],n,m,u,v;
double p[MAX_N];
double f[MAX_N][MAX_N][2];
void debug_floyd(void);
char ch;
int getnum(){
	ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar());
	int ret = 0;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) ret = ret * 10 + ch - 48;
	return ret;
}
void floyd()
{
	int i,k,j;
	for (i = 1; i <= u; i++) G[i][i] = 0;
	for (k = 1; k <= u; k++)
	  for (i = 1; i <= u; i++)
	    for (j = 1; j <= u; j++)
	      G[i][j] = min(G[i][j], G[i][k] + G[k][j]);
}

void solve()
{
	int i,j;
	for (i = 1; i <= n; i++)
	  for (j = 0; j <= m; j++)
	    f[i][j][0] = f[i][j][1] = 1e30;
	f[1][0][0] = 0.0; f[1][1][1] = 0.0;
	
	double ans = 999999999;
	for (i = 2; i <= n; i++)
	  for (j = 0; j <= m; j++)
	  {
	  	f[i][j][0] = min(f[i - 1][j][0] + G[a[i - 1]][a[i]], f[i - 1][j][1] + G[b[i - 1]][a[i]] * p[i - 1] + G[a[i - 1]][a[i]] * (1 - p[i-1]));
	  	if (j > 0) f[i][j][1] = min(f[i - 1][j - 1][0] + G[a[i - 1]][b[i]] * p[i] + G[a[i - 1]][a[i]] * (1 - p[i]), 
		  f[i - 1][j - 1][1] + G[b[i - 1]][b[i]] * p[i] * p[i - 1] + G[b[i - 1]][a[i]] * p[i - 1] * (1 - p[i]) + G[a[i - 1]][b[i]] * (1 - p[i - 1]) * p[i] + G[a[i - 1]][a[i]] * (1 - p[i - 1]) * (1 - p[i]));
	    
	  }
	  for (i = 0; i <= m; i++) ans = min(ans, min(f[n][i][0], f[n][i][1]));
	  printf("%.2f\n", ans);
}
int main()
{
	int i,x,y,z;
	n = getnum(); m = getnum(); u = getnum(); v = getnum();
	for (i = 1; i <= n; i++) a[i] = getnum();
	for (i = 1; i <= n; i++) b[i] = getnum();
	for (i = 1; i <= n; i++) cin >> p[i];
	memset(G, 0x3f, sizeof(G));
	memset(f, 0, sizeof(f));
	for (i = 1; i <= v; i++)
	{
		x= getnum(); y = getnum(); z = getnum();
		G[x][y] = G[y][x] = min(G[y][x], z);
	}
	
	floyd();
	solve();
	return 0;
}




第四题:组合数问题
考查知识点:杨辉三角,矩阵求和
做法:经过十多分钟探索,幸运地发现这道题的数值就是杨辉三角。由于最多才有2000*2000,而且题目有多次询问,所以可以直接把整个杨辉三角求出来。另外有一个重点就是再加上题目本身具有坑点——①答案的求法。 ②i可以小于j。但是i>j的时候就为0,没有意义重复计算了。
代码:
#include<iostream>
#include<cstdio>
#include<string.h>
using namespace std;

int t,k,n,m,i,j,z,f[2005][2005],ans,x1,rmb[2005][2005],maxn,maxm,temp;

int main(){
	freopen("problem.in","r",stdin);
	freopen("problem.out","w",stdout);
	cin >>t >>k; ans=0;
	for (i=1; i<=2000; i++) f[i][0]=1; f[1][1]=1; //初始化
	for (i=2; i<=2000; i++)
		for (j=1; j<=i; j++){
		  f[i][j]=(f[i-1][j-1]+f[i-1][j])%k;  //%k;
		} 
	for (j=1; j<=2000; j++){
		temp=0;
		for (i=1; i<=2000; i++){
			if (i<j) continue;
			if (f[i][j]==0) temp++;
			rmb[i][j]=rmb[i][j-1]+temp;
		}
	}
	for (z=1; z<=t; z++){
		cin >>n >>m;
		if (n<m) m=n;
		cout <<rmb[n][m] <<endl;
	}
	fclose(stdin); fclose(stdout);
	return 0;
}

第五题:蚯蚓
考查知识点:单调队列 堆排序
55分做法:此题有两个要点,知道了就知道怎么做了
找最大值由于每一次的操作都是要寻找把最大值进行操作,就自然而然地想到了最大堆。也可能是我赛前有专门复习过堆得原因。
除了被切的蚯蚓,其他蚯蚓长度+q就是除了被切得蚯蚓,其余的都要+q。这就非常尴尬了,就是在每一次操作后还要对每一个蚯蚓的长度加q,除了被切开的那两只。但是,这也让我们想到,全部都加上q,除了新切出来的蚯蚓不加,那么就是相当于这两条蚯蚓的长度比其他的少了q。诶!这就可以直接把这两条蚯蚓的长度减去q。然而,这不是实际的长度,只是一个相对长度。这样做的好处就是,处理后的每一条蚯蚓在计算实际长度的时候,都是加上同一个常数。ok,这样就稳稳地过了一半的数据。
时间复杂度:O(m*logn)
100分做法:
其实仔细想一下,还是发现其中的巧妙的性质——单调性。我们维护三个单调队列,一个是数据给出的蚯蚓的从小到大拍完后的队列,一个放被切后左端的新蚯蚓,另一个放被切后右端的蚯蚓。每一次从三个队列的队头查找最大值,切完后分别放置在放左右端蚯蚓的队列队尾为什么呢?因为每一次取出来的最大值,比上一个最大值小,而切割时是按一定比例的,切出来的左端和右端都分别小于等于上一次切割后产生的新蚯蚓的长度。因此,直接可以放置其相应队尾而队列仍然保持单调不增。至于除了被切的蚯蚓,其他蚯蚓长度+q这个问题,和上面堆做法中解释的做法一样,-q做好标记,切割或者询问时再计算实际长度。
代码:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
int cmp(int a,int b){
	if (a>b) return 1; else return 0;
}
long long a[100010],b[7000010],c[7000010];
long long temp,i,n,m,q,u,v,t,x1,x2,h1,h2,h3,ad;
int main(){
	freopen("earthworm.in","r",stdin);
	freopen("earthworm.out","w",stdout);
	cin >> n >> m >> q >> u >> v >> t;
	for (i = 1; i <= n; i ++) cin >> a[ i ];
	sort(a+1,a+n+1,cmp);
	h1=h2=h3=1; a[0]=n;
	for (i=1; i<=m; i++){
		ad=(i-1)*q;
		if (a[h1]>=b[h2] && a[h1]>=c[h3] && h1<=a[0]) {temp=a[h1]+ad; h1++;}
		  else if (b[h2] >= c[h3] ) {temp=b[h2]+ad; h2++;}
		    else {temp=c[h3]+ad; h3++;}
		x1=temp*u/v; x2=temp-x1;
		b[0]++; b[b[0]]=x1-ad-q;
		c[0]++; c[c[0]]=x2-ad-q;
		if (i % t==0) cout <<temp <<' ';
	}
	cout <<endl; ad=q*m; a[a[0]+1]=-999999999999; b[b[0]+1]=-999999999999; c[c[0]+1]=-999999999999;
	for (i=1; i<=n+m; i++){
		if (a[h1]>=b[h2] && a[h1]>=c[h3]) {temp=a[h1]; h1++;}
		else if (b[h2] >= c[h3]) {temp=b[h2]; h2++;}
		  else {temp=c[h3]; h3++;}
		if (i % t==0) cout <<temp+ad <<' ';
	}
	fclose(stdin); fclose(stdout);
	return 0;
}

第六题:愤怒的小鸟
考查知识点:状态压缩DP  位运算


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值