NOIP2014提高组复赛解题报告

Day1

T1生活大爆炸版剪刀石头布:模拟,水;

T2联合权值:树形DP,水;

T3Flappy Birds:

这道题我当时算时间复杂度算错了,O(nm^2)的时间复杂度给算成O(nm)了,所以根本就没想优化,以后①算时间复杂度的时候要小心一点了

其实正解也是很简单的,只是在直译式DP的基础上做了一点小优化。

直译式DP:设f(i,j)为到达当前点的最小步数,则

f(i,j)->f(i-1,j+Y[i-1])

      ->f(i-1,j-k*X[i-1])+k,k>0且j-k*X[i-1]>0

完全背包式优化:

f(i,j)->f(i-1,j-X[i-1])+1

      ->f(i,j-X[i-1])+1

      ->f(i-1,j+Y[i-1])

(顺序不能颠倒)

“因为f(i,j)这个状态实际上可以由f(i,j-X[i-1])这个状态转移而来"——GTY     orz

②这种多步与一步等价的思想实际上是非常实用和需要我掌握的。

回来以后写的AC代码:

#include
using namespace std;
#include
#include
#include
#include
typedef short hd;
char * ptr=(char *)malloc(1000000);
int f[10001][1001];
inline void in(hd &x){
	while(*ptr<'0'||*ptr>'9')++ptr;
	x=0;
	while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
int main(){
	
	hd P[10000],down[10001],up[10001],X[10000],Y[10000],N,M,K,tmp,i,j;
	int MAXN=2000000000;
	
	freopen("birda.in","r",stdin);
	freopen("birda.out","w",stdout);
	
	//读入 
	fread(ptr,1,1000000,stdin);
	in(N),in(M),in(K);
	for(i=0;iY[i-1];--j)f[i][j-Y[i-1]]=min(f[i][j-Y[i-1]],f[i-1][j]);
		for(j=0;j<=down[i];++j)f[i][j]=MAXN;
		for(j=up[i];j<=M;++j)f[i][j]=MAXN;
		
		//判断当前列是否可达
		j=1;
		while(j<=M&&f[i][j]>=MAXN)++j;
		if(j>M)break;
	}
	if(i>N)printf("1\n%d",*min_element(f[N]+1,f[N+1]));
	else printf("0\n%d",lower_bound(P,P+K,i)-P);
}


Day2

T1无线网络发射器选址:当时蛋疼写了个DP,实际上暴力就可以,水。

T2寻找道路:我发现D2好像一直很蛋疼,又写了个DFS+SPFA,实际上DFS+BFS就可以了。

T3:解方程:

这是一道很奇怪的题,我刚看到他的时候没有一点思路。。除了暴力。然后就想写个50分的压位高精暴力,结果还写残了。。所以只得了30分。回来的路上听到学长们讨论各种取模+乱搞。直接取模+暴力的话,O(nm)的时间复杂度实际上是可以得70分的。

那么就来说一下正确解法吧。——来自,各种大神的题解。

引理:若f(x)≡0(mod p),则f(x+p)≡0(mod p).

所以,我们可以先取一个小质数P,求出[1,P)内所有的解,然后可以通过+P推得在[1,m]内所有符合f(x)≡0(mod P),再用另一个大质数p,用来check每个解即可。

时间复杂度的话,应该是难以估计的,因为模质数本身就是一个用正确率换时间的做法嘛。。对于普通一点的数据的话,应该是可以达到O(n*p+n*n*m/p)。我们发现这玩意儿竟然是我刚学的对勾函数。。当p最接近(n*m)^(1/2)时,时间复杂度最低。此时O(10^2*10^4+10^4*10^6/10^4)≈O(2*10^6).

代码:

#include
using namespace std;
#include
#include
#include 
#include
int main(){
	const int P=10007,p=100000009;
	int i,j,m,n,a[101]={0},b[101]={0},mi[101],ans[101],tmp,lst[10008],h=0,t=0;
	ans[0]=1,mi[0]=1;
	
	freopen("equationa.in","r",stdin);
	freopen("equationa.out","w",stdout);
	
	char * ptr=(char *)malloc(2000000);
	scanf("%d%d",&n,&m);
	fread(ptr,1,2000000,stdin);
	for(i=0;i<=n;++i){
		bool flag=0;
		while(*ptr<'0'||*ptr>'9')
			if(*ptr++=='-')
				flag=1;
		while(*ptr>47&&*ptr<58){
			a[i]=(a[i]*10+*ptr-'0')%P;
			b[i]=(b[i]*10+*ptr++-'0')%p;
		}
		if(flag){
			a[i]=-a[i];
			b[i]=-b[i];
		}
	}
	for(i=1;i



③这道题应该说让我对hash有了全新的认识。其优化暴力的方法也是很巧妙的,将其花费n*p的时间复杂度模一个小质数之后,我们会发现我们可以判定在(p,m]的范围内有些值是必定不可取的了,即若f(x)≠0(mod p),f(x+p)≠0(mod p),这样便达到了对暴力的优化。

你可能感兴趣的:(DP,hash)