NOIP2015提高组复赛 解题报告

Day 1

T1  直接根据题目描述的模拟就可以了,水。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,m,i,j,num;
int a[50][50];
int main(){
	scanf("%d",&n);
	m=(n/2)+1;
	a[1][m]=1;
	num=1;
	i=1; j=m;
	while (num<n*n){
		if (i==1&&j!=n) {a[n][j+1]=++num; i=n; ++j;}
		else if (i!=1&&j==n) {a[i-1][1]=++num; --i; j=1;}
		else if (i==1&&j==n) {a[i+1][j]=++num; ++i;}
		else if (i!=1&&j!=n){
			if (!a[i-1][j+1]) {a[i-1][j+1]=++num; --i; ++j;}
			else {a[i+1][j]=++num; ++i;}
		}
	}
	for (int i=1;i<=n;++i){
		for (int j=1;j<n;++j)
		  printf("%d ",a[i][j]);
		printf("%d\n",a[i][n]);
	}
	return 0;
}


T2  读了一遍题就看出来是最小环。刚开始忽略了有好几个联通块,大数据跑不对。最后写了个并查集+dfs,AC。水。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
#define inf 2100000000
#define N 200005
using namespace std;
int n,ans,y;
int f[N],next[N],h[N];
bool b[N];
inline int in(){
	int x=0; char ch=getchar();
	while (ch<'0'||ch>'9') ch=getchar();
	while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
	return x;
}
int find(int x){
	if (x==f[x]) return x;
	f[x]=find(f[x]);
	return f[x];
}
void merge(int x,int y){
	int f1=find(x);
	int f2=find(y);
	f[f1]=f2;
}
void dfs(int x,int dep){
	b[x]=true; h[x]=dep;
	if (!b[next[x]])
	  dfs(next[x],dep+1);
	else{
		int k=h[x]-h[next[x]]+1;
		ans=min(ans,k);
	}
}
int main(){
	n=in(); ans=inf;
	for (int i=1;i<=n;++i)
	  f[i]=i;
	for (int i=1;i<=n;++i){
		y=in();
		next[i]=y;
		if (find(i)!=find(y))
		  merge(i,y);
		else{
			memset(b,0,sizeof(b));
			dfs(i,1);
		}
	}
	printf("%d",ans);
	return 0;
}

T3  时限2s内存1G就知道这道题一定很神。刚开始看到了之后一点思路都没有,以为是dp但是发现状态太多压不下。而且还有多组数据一旦跑不对就挂了。30分打表。
后来听到TA大爷说就是个dfs,把每一种情况编上号之后搜索就可以了。
好像也有dfs+dp的做法,不过我觉得这个更好理解。
1、单顺子 2、双顺子 3、三顺子 4、三带一、三带二  5、四带二、四带两对
由于每张牌的数量最多有4张,所以能保证这一种牌只要有就一定能一次打出去,所以出牌总次数的上限就是手牌的种类数。而每一次dfs都有一个新的上限,就是当前步数+现有的手牌的种类数。dfs过程中要进行最优化剪枝。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int T,n,a[30],ans,x,y;
inline bool check(){
	for (int i=1;i<=15;++i)
	  if (a[i]) return false;
	return true;
}
inline void dfs(int dep){
	int i,j,k,kind,sum=0;
	if (dep>ans) return;
	if (check()){
		ans=min(ans,dep);
		return;
	}
	for (int i=1;i<=13;++i)
	  if (a[i]) ++sum;
	if (a[14]+a[15]) ++sum;
	ans=min(ans,dep+sum);
	for (kind=1;kind<=5;++kind){
		if (kind==1)
		  for (i=1;i<=8;++i){
		  	sum=0;
		  	for (j=i;j<=i+3;++j)
		  	  if (a[j]) ++sum;
		  	if (sum!=4) continue;
		  	for (j=i+4;j<=12&&a[j];++j){
		  		for (k=i;k<=j;++k) --a[k];
		  		dfs(dep+1);
		  		for (k=i;k<=j;++k) ++a[k];
		  	}
		  }
		if (kind==2)
		  for (i=1;i<=10;++i)
		    if (a[i]>=2&&a[i+1]>=2)
		      for (j=i+2;j<=12&&a[j]>=2;++j){
		      	for (k=i;k<=j;++k) a[k]-=2;
		      	dfs(dep+1);
		      	for (k=i;k<=j;++k) a[k]+=2;
		      }
		if (kind==3)
		  for (i=1;i<=11;++i)
		    if (a[i]>=3)
			  for (j=i+1;j<=12&&a[j]>=3;++j){
			    for (k=i;k<=j;++k) a[k]-=3;
			    dfs(dep+1);
			    for (k=i;k<=j;++k) a[k]+=3;
			  }
		if (kind==4){
			for (i=1;i<=15;++i)
			  if (a[i]>=3){
			  	a[i]-=3;
			  	for (j=1;j<=15;++j)
			  	  if (a[j]>=2)
			  	    a[j]-=2,dfs(dep+1),a[j]+=2;
			  	a[i]+=3;
			  }
			for (i=1;i<=15;++i)
			  if (a[i]>=3){
			  	a[i]-=3;
			  	for (j=1;j<=15;++j)
			  	  if (a[j])
			  	    --a[j],dfs(dep+1),++a[j];
			  	a[i]+=3;
			  }
		}
		if (kind==5){
			for (i=1;i<=15;++i)
			  if (a[i]>=4){
			  	a[i]-=4;
			  	for (j=1;j<=15;++j)
			  	  if (a[j]>=2){
			  	  	a[j]-=2;
			  	  	for (k=j;k<=15;++k)
			  	  	  if (a[k]>=2)
			  	  	  a[k]-=2,dfs(dep+1),a[k]+=2;
			  	  	a[j]+=2;
			  	  }
			  	a[i]+=4;
			  }
			for (i=1;i<=15;++i)
			  if (a[i]>=4){
			  	a[i]-=4;
			  	for (j=1;j<=15;++j)
			  	  if (a[j]){
			  	  	--a[j];
			  	  	for (k=j;k<=15;++k)
			  	  	  if (a[k])
			  	  	    --a[k],dfs(dep+1),++a[k];
			  	  	++a[j];
			  	  }
			  	a[i]+=4;
			  }
		}
	}
}
int main(){
	scanf("%d%d",&T,&n);
	while (T--){
		memset(a,0,sizeof(a));
		for (int i=1;i<=n;++i){
			scanf("%d%d",&x,&y);
			if (!x) ++a[y+13];
			if (x==1||x==2) ++a[x+11];
			if (x>=3) ++a[x-2];
		}
		ans=0;
		for (int i=1;i<=13;++i)
		  if (a[i]) ++ans;
		if (a[14]+a[15]) ++ans;
		dfs(0);
		printf("%d\n",ans);
	}
}

Day2

T1  二分答案+贪心枚举,考前做过原题,但是还是调了一会,水。
【代码】
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 50005
using namespace std;
int L,n,m,l,r,mid,num;
int a[N];
int ok(int x){
	int ans=0,k=0;
	for (int i=1;i<=n+1;++i)
	  if (a[i]-a[k]<x)
	    ans++;
	  else k=i;
	return ans;
}
int main(){
	scanf("%d%d%d",&L,&n,&m);
	for (int i=1;i<=n;++i)
	  scanf("%d",&a[i]);
	sort(a+1,a+n+1);
	a[0]=0; a[n+1]=L;
	l=0; r=L;
	while(l<=r){
		mid=(l+r)/2;
		num=ok(mid);
		if (num<=m) l=mid+1;
		else r=mid-1;
	}
	printf("%d",l-1);
}

T2  一眼就能看出来是dp,但是dp学的不好,想了一会没大有思路,就只写了30分的部分分。dfs没跑出来就只得了10分。
动规的思路看了ShallWe的代码才勉强理解,要是让我自己想是绝对想不到的,因为我当时连前缀和优化是什么都不怎么清楚。
f[i][j][k][l]表示第一个串的前i个,第二个串的前j个,分成k段,还有一维是前缀和,也可理解为选还是不选。注意这里的第一维要加一个滚动数组。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
#define N 1005
#define M 205
#define p 1000000007
using namespace std;
char s1[N],s2[M];
int n,m,k;
int f[2][M][M][2];
inline void in(){
	char ch=getchar();
	while (ch<'a'||ch>'z') ch=getchar();
	s1[1]=ch;
	for (int i=2;i<=n;++i) s1[i]=getchar();
	ch=getchar();
	while (ch<'a'||ch>'z') ch=getchar();
	s2[1]=ch;
	for (int i=2;i<=m;++i) s2[i]=getchar();
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	in();
	f[0][0][0][0]=1;
	f[1][0][0][0]=1;
	for (int i=1;i<=n;++i)
	  for (int j=1;j<=min(i,m);++j)
	    for (int l=1;l<=k;++l){
	    	int now=i&1,last=(i-1)&1;
	    	if (s1[i]==s2[j]){
	    		f[now][j][l][1]=((f[last][j-1][l-1][0]+f[last][j-1][l-1][1])%p+f[last][j-1][l][1])%p;
	    		f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
	    	}
	    	else{
	    		f[now][j][l][0]=(f[last][j][l][0]+f[last][j][l][1])%p;
	    		f[now][j][l][1]=0;
	    	}
	    }
	printf("%d",(f[n&1][m][k][0]+f[n&1][m][k][1])%p);
}

T3  第一眼觉得是倍增,然后各种写,越写越觉得太繁琐,好像实现不了。最后只写了一个链,时间复杂度还算错了,所以只有5分。其实还是有很多部分分的,但是考试时没有时间写了。
考完试思考了一下感觉还是做不大出来,听学长说了二分,不是很懂,下面的来自Rivendell的博客: http://www.cnblogs.com/Rivendell/p/4972055.html

总的来说,NOIP2015做的还是可以的,简单一点的题没有丢分,但是有一些思考复杂度高一点的题目没有做出来,下一步应该加强训练。

你可能感兴趣的:(NOIP2015提高组复赛 解题报告)