解题报告 SNNU_ACMer寒假16~18号-水题5道

A - qj的寻找爱情之旅

 题目链接:http://acdream.info/problem?pid=1207

 

题意: 

已知 H[1]=A  H[i] = (H[i−1] + H[i+1])/2 − 1, 其中 1 < i < N

对于每个点来说 Hi ≥ 0, 其中1 ≤ i ≤ N

求 :H[n]=B 使 B 最小。

 

由题意可推出

h(i) = 2h(i-1) - h(i-2) + 2   ( i >= 3)

但h(2)不知道,而h(2)一旦知道,该数列也就处处可求了,也就是说本题需要通过枚举h(2)的值来找到最小的H(n) = B。

因为这道题有两位小数,所以需要二分答案。

不难看出,因为h(i) = 2h(i-1) - h(i-2) + 2   ( i >= 3) , 所以当h(i) < 0 的时候,说明 h(i-2)太大,也就是h(2)太大,需要在左半边寻找。

如果所有的h(i)都>0,则扩大h(2) ,即在右半部分寻找,直到找到最小值。
注意浮点数误差处理,以及找到二分的右边界,求解可用滚动数组。

代码如下:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define eps 1e-8
using namespace std;

int main()
{
	// f(i) = 2f(i-1) - f(i-2) + 2   ( i>= 3 )
	int n ;
	double a ;
	cin >> n >> a ;
	double l = 0 , r = 10000 , mid = ( l + r ) / 2;
	double minn = 1e9 ;
	while( l - r <= eps  )
		{
			double c = a , d = mid  , e ;
			bool flag = true;
			for( int i =3 ; i <= n ; i++ )
				{
					e = 2*d - c + 2 ;
					c = d ;
					d = e ;
					if( e < eps  )
						{
							l = mid+eps ;
							mid = ( l + r ) / 2  ;
							flag = false  ;
							break ;
						}
				}
			if( flag && e - minn < eps)
				{
					r = mid-eps;
					mid = ( l+r) / 2 ;
					minn = e ;
					continue ;
				}
		}
	printf("%.2lf\n",minn);
	return 0 ;
}

  时间复杂度: O(n*logn)

B - A-B

 题目链接:http://acdream.info/problem?pid=1671

 

题意:

任意给定BC10000到AD10000年的两个日期(精确到秒),求他们之间相隔的秒数。

这道题我不会…GG。

看了下没人A过这道题,只有出题人用JAVA A了这道题。这道题真的不是考察JAVA语法的么……

做了一晚上,应该是道大模拟。

对于都是AD的情况,是模拟竖式减法(经自己测试能过),即就是AD2 - AD1 ,注意处理借位问题,借位问题要注意month的时候28 29 30 31 ……

BC和AD不太会搞。

BC我一开始认为可以看做是 BC1 - BC2 , 可是BC只有year是倒流的,如果直接这样强行减,整个时光都是倒着流了2333,测试了下,有问题。

BC 到AD的情况更不会了 ,一开始认为是可以看做 ( AD - AD1年1月1日0时0分0秒 )+  ( BC - BC1年1月1日23时59分59秒  ) 

然后测试一下血崩。

 

第一波代码 都是AD情况可过的:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define eps 1e-8
using namespace std;
bool runnian( int i )
{
	if( i % 4 == 0 && i % 100 != 0 )
		return true;
	else if ( i % 400 == 0  && i % 100 == 0)
		return true ;
	else
		return false ;
}
int m( int y ,int i )
{
	if( i == 1 || i == 3 || i == 5 || i== 7 || i==8 || i==10 || i==12 )
		return 31;
	else if ( i == 4 || i==6 || i==9 || i==11 )
		return 30 ;
	else if ( runnian(y) && i == 2 )
		return 29 ;
	else
		return 28 ;
}

int cal( int y,int m1 ,int m2 )
{
	int ans = 0 ;
	for( int i = m1 ; i < m2 ; i++)
		{
			ans+= m(y,i);
		}
	return ans ;
}

int cal2( int year1, int year2)
{

	int count= 0;
	for( int i = year1 ; i<year2; i++)
		{
			if( runnian(i))
				count++;
		}
	return count;
}
int main()
{
	string a1,a2;
	int year1,year2,mon1,mon2,day1,day2,hour1,hour2,min1,min2,sec1,sec2;
	while( cin >> a1 >> year1 >> mon1 >> day1 >> hour1 >> min1  >> sec1
	        >> a2 >> year2 >> mon2 >> day2 >> hour2 >> min2  >> sec2 )
		{
			long long int ans = 0 ;
			if( a1 == "AD" && a2 == "AD" )
				{
					while( sec2 < sec1 )
						sec2+=60,min2--;
					ans += sec2 - sec1;
					while( min2 < min1 )
						min2+=60,hour2--;
					ans += (min2-min1)*60;
					while( hour2 < hour1)
						hour2+=60,day2--;
					ans += (hour2-hour1) * 60 * 60 ;
					while( day2 < day1)
						mon2--,day2+=m(year2,mon2);
					ans+= (day2-day1) * 24 * 60 * 60  ;
					while( mon2 < mon1 )
						mon2+=12 , year2--;
					ans+= cal(year2,mon1,mon2) * 24 * 60 * 60  ;

					ans+= (year2 - year1 - cal2(year1,year2) ) * 365 * 24 * 60 * 60  ;
					ans+= cal2(year1,year2) * 366  * 24 * 60 * 60  ;
				}
			else if( a1 == "BC" && a2 == "BC" )
				{
					while( sec2 < sec1 )
						sec2+=60,min2--;
					ans += sec2 - sec1;
					while( min2 < min1 )
						min2+=60,hour2--;
					ans += (min2-min1)*60;
					while( hour2 < hour1)
						hour2+=60,day2--;
					ans += (hour2-hour1) * 60 * 60 ;
					while( day2 < day1)
						mon2--,day2+=m(year2,mon2);
					ans+= (day2-day1) * 24 * 60 * 60  ;
					while( mon2 < mon1 )
						mon2+=12 , year2--;
					ans+= cal(year2,mon1,mon2) * 24 * 60 * 60  ;

					ans+= (year2 - year1 - cal2(year1,year2) ) * 365 * 24 * 60 * 60  ;
					ans+= cal2(year1,year2) * 366  * 24 * 60 * 60  ;
				}
			else 	if( a1 == "BC" && a2 == "AD" )
				{
					ans+= sec2 ;
					ans+= min2 * 60 ;
					ans+=hour2 * 60 * 60 ;
					ans+=(day2-1)*24*60*60;
					ans+= cal(year2,1,mon2) * 24 * 60 * 60 ;
					ans+= (year2 - 1 - cal2(1 , year2 ) ) * 365 * 24 * 60 * 60 ;
					ans+= cal2(1,year2) * 366 * 24 * 60 * 60 ;


				}
			cout << ans << endl ;
		}
	return 0 ;
}

  写的太凌乱了,想改写一下,结果改写后啥都过不了了GG,

改写了一部分的(怪自己不会OOP,代码都写不优美):

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
using namespace std ;
struct date
{
	string s;
	int year,month,day,hour,min,sec;
};

bool scan( struct date &a)
{
	return cin >> a.s >> a.year >> a.month >> a.day >> a.hour >> a.min >> a.sec ;
}

bool runnian ( int y )
{
	if( ( y % 4 == 0 && y % 100 != 0 ) || ( y % 400 == 0 || y % 100 == 0 ) )
		return true;
	else
		return false ;
}

bool monthday(int y,int i  )
{
	switch( i)
		{
			case 1:
			case 3 :
			case 5 :
			case 7 :
			case 8 :
			case 10 :
			case 12 :
				return 31 ;
			case 4 :
			case 6 :
			case 9 :
			case 11 :
				return 30 ;
			case 2 :
				if( runnian(y) )
					return 29 ;
				else
					return 28 ;
		}

}

int days( int y , int m1 , int m2 )
{
	int ans = 0 ;
	for( int i = m1 ; i < m2 ; i++)
		{
			ans+= monthday( y , i );
		}
	return ans ;
}

int c( int y1 , int y2 )
{
	int count = 0 ;
	for( int i = y1 ; i < y2 ; i++)
		if( runnian(i) )
			count++;
	return count ;
}
long long int calculate( struct date a , struct date b )
{
	long long int ans = 0 ;

	while( b.sec < a.sec )
		b.sec+=60,b.min--;
	ans+= b.sec - a.sec;
	while( b.min < a.min)
		b.min+=60 , b.hour--;
	ans+= (b.min-a.min)*60;
	while( b.hour < a.hour)
		b.hour+=24,b.day--;
	while( b.day < a.day )
		b.month--,b.day+= monthday(b.year,b.month) ;
	ans+= ( b.day - a.day ) * 24 * 60 * 60 ;
	while( b.month < a.month)
		b.month+=12,b.year--;
	ans+= days(b.year,a.month,b.month) * 24 * 60 * 60 ;
	
	ans+= ( b.year - a.year  - c( a.year, b.year) ) * 365 * 24 * 60 * 60 ;
	ans+= c(a.year , b.year ) * 366 * 24 * 60 * 60 ;
	return ans ;
}
int main()
{
	struct date a , b ;
	while( scan(a) && scan(b)  )
		{
			cout << calculate( a , b ) << endl ;
		}
}

  

C - 面面数

 题目链接:http://acdream.info/problem?pid=1062

题意: 经公共点,且任意三个平面不过同一直线把空间可最多分为多少块?

给出块数N,求最少需要多少个平面n。

这道题不难得出通项公式 f(n) = f(n-1) + 2*(n-1)   ( 这里考察前三个面就可以,第一二个面确定,第三个面是和他们交线划分,注意一下划分比较巧妙,3个面以后都是和他们交线分,参考直线分割平面问题)

一开始直接暴力试探,结果TLE了

代码如下(错误):

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long int f( int i )
{
	if( i == 0 )
		return 1 ;
	else if ( i == 1 )
		return 2 ; 
	else
		return f( i - 1 ) + 2*(i-1);
}
int main()
{
	int t ;
	cin >> t ;
	while(t--)
	{
		long long int n ;
		cin >> n ;
		int i = 0 ;
		while( f(i) < n )
			i++;
		cout << i << endl ;
	}
	return 0 ; 
 } 

  第二次以为是递归开销太大,于是求了通项公式(错误):

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long int f( int i )
{
	if( i == 0 )
		return 1 ;
	else if ( i == 1 )
		return 2 ; 
	else
		return (2*i*i-i)  / 2 + 1 ; 
}
int main()
{
	int t ;
	cin >> t ;
	while(t--)
	{
		long long int n ;
		cin >> n ;
		int i = 0 ;
		while( f(i) < n )
			i++;
		cout << i << endl ;
	}
	return 0 ; 
 } 

  还是TLE,以为是枚举不行,于是改二分(错误代码):

 

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long int f( int i )
{
	if( i == 0 )
		return 1 ;
	else if ( i == 1 )
		return 2 ; 
	else
		return (2*i*i-i)  / 2 + 1 ; 
}
int main()
{
	int t ;
	cin >> t ;
	while(t--)
	{
		long long int n ;
		cin >> n ;
		long long int l = 0 , r = 100000 , mid = ( l + r ) >> 1 ;
		bool flag = false ;
		while( l <= r )
		{
			if( f( mid ) > n )
			{
				r = mid-1;
				mid = ( l + r ) >> 1 ;
			}
			else if( f( mid ) < n )
			{
				l = mid+1 ;
				mid = ( l + r ) >> 1 ;
			}
			else if( f( mid ) == n )
			{
				flag = true;
				break ;
			}

		}
		if( flag )
			cout << mid << endl ;
		else 
			cout << mid+1 << endl ;
	}
	return 0 ; 
 } 

  改二分后WA了好3 4发……GG。

 

最后发现是我通项公式求错了……

 

改正后(AC):

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
long long int f( int i )
{
	if( i == 0 )
		return 1 ;
	else if ( i == 1 )
		return 2 ; 
	else
		return i*(i-1) + 2 ; 
	}
int main()
{
	int t ;
	cin >> t ;
	while(t--)
	{
		long long int n ;
		cin >> n ;
		if( n == 0 )
			{
				cout <<  1 << endl ;
				continue ;
			}
		long long int l = 0 , r = 61000 , mid = ( l + r ) >> 1 ;
		bool flag = false ;
		while( l <= r )
		{
			if( f( mid ) > n )
			{
				r = mid-1;
				mid = ( l + r ) >> 1 ;
			}
			else if( f( mid ) < n )
			{
				l = mid+1 ;
				mid = ( l + r ) >> 1 ;
			}
			else if( f( mid ) == n )
			{
				flag = true;
				break ;
			}

		}
		while( f(mid) < n )
			mid++;
		cout << mid << endl ; 
	}
	return 0 ; 
 } 

  这里通项公式求法是:

f(1) - f(0) = 1

f(2) - f(1) = 2

f(3) - f(2) = 4

.....

.....

f(n) - f(n-1) = 2*(n-1)

然后把上面所有全部加起来变成

f(n) - f(0) = 1 + 2 + 4 + .... + 2*(n-1) 

然后化简就好了 (高中数列知识)

 时间复杂度:O(logn)

 

事后看了别人题解发现,其实可以预处理一个array[60000]的数组,保存里面每一个的结果就可以(事先算出当60000的时候刚好是最大值N 2e10),比我这简单到不知道哪里去了。

D - Gotta Catch Em' All!

 题目链接:http://codeforces.com/problemset/problem/757/A

 

题意:给定一个字符串,让你把里面所有字母随意组合,问能组合出多少个【Bulbasaur】 单词?

 

数数就好了,然后取最小值(注意双数单词需要除以2)

这道题WA1发是以为样例1就是这个单词。。结果多数1个b GG。

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
string s ; 

int main()
{
	cin >> s ;
	int B = 0 , u = 0, l = 0 , b = 0 , a=0,ss=0,r=0;//Bulbasaur
	for( int i = 0 ; i < s.size() ; i++)
	{
		switch( s[i] )
		{
			case 'B' : B++ ; break ;
			case 'u' : u++ ; break ;
			case 'l' : l++ ; break ;
			case 'b' : b++ ; break ;
			case 'a' : a++ ; break;
			case 's' : ss++ ; break;
			case 'r' : r++ ; break ;
		}
	}
	int minn = B;
	minn = min(minn,u/2);
	minn = min(minn,l);
	minn = min(minn ,b);
	minn = min(minn,a/2);
	minn = min(minn,ss);
	minn = min(minn,r);
	cout << minn << endl ;
	return 0 ; 
}

 

 时间复杂度:O(n)

E - New Year and Hurry

 题目链接:http://codeforces.com/problemset/problem/750/A

 

题意: 给出n,k ,给你240 - k 分钟时间做题,已知做第i题的时间需要5*i,共有n道题。

求最多做多少道题?

 

贪心求出240 - k的情况下最多能做出多少道题,然后和总题数取最小值:

 

AC代码:

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

int main()
{
	int n , k;
	cin >> n >> k ;
	int time = 4 * 60 - k , question =0 ; 
	while( time -  5 * (question+1)>= 0 )
		question++,time -= 5*question ;
	cout << min( question , n )  << endl ;
	return 0 ; 
}

  时间复杂度O(n)

————————

 

总结:除了B题,其他题都还好。B题本来想做首A的,然而做不到了QAQ,想等别人题解,我写的太繁琐复杂了。

 

posted on 2017-01-18 16:48  Dark猫  阅读(173)  评论(0)    收藏  举报

导航