a,b题不说。
c题思路是每次枚举俩个点,用半径R确定最大的圆(这样的圆有俩个,求圆心手算有点小麻烦),更新最大值,3次方的,100个点,不会超时。
D题是枚举+贪心,所有物品一共只能是N+1种被拿的情况:要么全是用R(该位子是若用右手标记R,若用左手标记L):RRR...RRR,或者第一个物品用L:LRRR...RR,.....依次到LLLLLL..LLL,一个序列来记录每个物品是被左手还是右手拿。枚举所有序列,如LLL...RRR,贪心:那必然是一次L一次R,多出来的需要额外开销能量。如LLLLRR,多2次R,多一次开销qr(先R)。这样是o(n)的算法。
E题,枚举aj,然后向俩边搜索,往右边搜索的时候,若该数是刚刚左边数的倍数,则跳过枚举。详见代码则明。
F题,(CF 126D)不错的题。求任意一个数表示成不同的斐波那契数之和有几种方案。思路:用该数去减不大于它的最大的斐波那契数,得到新的数,然后用该数重复上述过程,直到该数为0,用这种方法得到一种拆分方法。然后巧妙的是:用一个01序列来表示一种拆分:如14=13+1,则表示为100001(1表示取,0,不取,从左向右,1,2,3,5,8,13,...),
那么1可以表示成左边两个1,001 -》110。然后又多少种拆分?下面用动态规划,dp[i][1]:表示该拆分中,从左往右第I个1取有几种方案,DP[I][0],表示不取有几种方案。则: 第i个1若取,则第i-1个1到第i个1之间必取0,所以得到:dp[i][1]=dp[i-1][1]+dp[i-1][0]; 若不取,则看第i-1个1与该位之间有几个0,若第I-1个1取,设有K个0,则第I个1必有
k/2种拆分方案,由乘法原理,得:dp[i-1][1]*((id[i]-id[i-1]-1)/2),若i-1个1不取,则又多一个0,则:dp[i-1][0]*((id[i]-id[i-1])/2); 故 :dp[i][0]=dp[i-1][1]*((id[i]-id[i-1]-1)/2)+dp[i-1][0]*((id[i]-id[i-1])/2);(id【i】记录第i个1所在的位子号)
代码详见:
#include<iostream> //f题 #include<vector> #include<algorithm> using namespace std; long long fib[100]; void got() { fib[1]=fib[0]=1; for(int i=2;i<88;i++) { fib[i]=fib[i-1]+fib[i-2]; } } int dp[88][2]; int main() { got(); int T; cin>>T; while(T--) { long long n; cin>>n; vector<int>id; //id【i】记录第i个1所在的位子号 for(int i=87;i>0&&n!=0;i--) //获得一种边界拆分 { if(n>=fib[i]) { n=n-fib[i]; id.push_back(i); } } reverse(id.begin(),id.end()); dp[0][1]=1; dp[0][0]=(id[0]-1)/2; for(int i=1;i<id.size();i++) { dp[i][1]=dp[i-1][1]+dp[i-1][0]; dp[i][0]=dp[i-1][1]*((id[i]-id[i-1]-1)/2)+dp[i-1][0]*((id[i]-id[i-1])/2); } cout<<dp[id.size()-1][1]+dp[id.size()-1][0]<<endl; } return 0; }
原文地址:http://blog.csdn.net/u011498819/article/details/37600697