解题报告 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,想等别人题解,我写的太繁琐复杂了。
浙公网安备 33010602011771号