51nod二级算法题全部题解
来源:互联网 发布:身份证nfc读取 软件 编辑:程序博客网 时间:2024/06/08 17:24
首先对二级算法做一个总结吧,大体上都是一些不是很难的题,但是很有助于提高的,毕竟对于我这样的萌新来说。既然题已经刷完了,当然还是写一篇总结,对学到的经验进行一下总结。。。不然下次碰到不会的还是不会就很尴尬了。
下面进入正文:
1873 初中的算术:
就是大数运算,如果想写的同学可以用C++去写写,也不是很难就是麻烦点,就当是练手了,这方面的博客很多直接百度就好了。这里笔者比较懒就直接用Java暴力过了。。。Java大法好啊。
import java.math.BigDecimal;import java.util.*;public class MAIN { public static void main(String args[]) { Scanner cin = new Scanner(System.in); double m = cin.nextDouble(); int n = cin.nextInt(); BigDecimal ans=new BigDecimal("1"); for(int i = 0; i < n; i++) ans = ans.multiply(BigDecimal.valueOf(m)); String s=ans.stripTrailingZeros().toPlainString(); int i=0; if(s.charAt(0)=='0'&&s.charAt(1)=='.') i=1; for(;i<s.length();i++) System.out.print(s.charAt(i)); }}
二进制的重要性,存0-1e6的这些指数的出现次数,然后不停的除2进位就可以,从低位开始,只要是留下的某一位进不上去剩下一个了,那就是一定要花费一次机会去运送这个。需要注意的是:1,题中输入的货物的2^wi的wi不是货物的重量;2,数组最大要开大点,后面可能会进好几次超过1e6.
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int Max = 1000024;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;int n;ll arr[Max];int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d", &n)) { memset(arr, 0, sizeof(arr)); for(int i=0; i<n; i++) { int t; scanf("%d", &t); arr[t]++; } int ans = 0; for(int i=0; i<Max; i++) { arr[i+1] += arr[i]/2; arr[i] %= 2; if(arr[i] == 1) ans++; } cout<<ans<<endl; } return 0;}
核心就是每次失去一个可用位置k,对于整个区间的影响。也就是只需要计算出来k位于的区间[l, r]本来的可容纳战舰数再减去k被占据之后,可容纳的战舰数。有点像莫对算法里的区间转移的问题。解决了这个就按题意模拟就行了
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int Max = 1e6+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;int n, k, a;ll arr[Max];int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d%d%d", &n,&k,&a)) { memset(arr, 0, sizeof(arr)); int m; scanf("%d", &m); int ans = 0; int MaxNum = (n+1)/(a+1); for(int i=1; i<=m; i++) { int t, prenum, curnum;//分区间之前能容纳的战舰数,分区间之后能容纳的战舰数 scanf("%d", &t); if(ans) continue; arr[t] = 1; int left=t-1, right=t+1; while(left>=1 && arr[left]==0) left--; while(right<=n && arr[right]==0) right++; prenum = (right-left)/(a+1); curnum = (t-left)/(a+1)+(right-t)/(a+1); MaxNum -= prenum - curnum; if(MaxNum<k && ans==0) ans = i; } if(ans) cout<<ans<<endl; else cout<<-1<<endl; } return 0;}
1489 蜥蜴和地下室:
dfs,这个没啥好说的,就是多练练吧,数据量很小,dfs就好了
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int Max = 1e6+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;int n, a, b, ans;int arr[Max];void dfs(int x, int num)//第x个弓箭手,当前的已使用火球数为num{ if(x == n-1) { int t1 = arr[x-1]/a+1; int t2 = arr[x]/b+1; if(arr[x-1]<0) t1 = 0; if(arr[x]<0) t2 = 0; num += max(t1, t2); ans = min(ans, num); return; } if(num>ans) return; int pre = arr[x-1]/b+1; if(arr[x-1] < 0) pre = 0; int cur = arr[x]/a+1; if(arr[x] < 0) cur = 0; cur = max(cur, pre);//在这里必须保证把x-1位置的弓箭手杀死 for(int i=pre; i<=cur; i++) { arr[x+1] -= b*i; arr[x] -= a*i; dfs(x+1, num+i); arr[x+1] += b*i; arr[x] += a*i; }}int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d%d%d", &n,&a,&b)) { ans = INF; for(int i=0; i<n; i++) scanf("%d", arr+i); dfs(1, 0); cout<<ans<<endl; } return 0;}
1629 B君的圆锥:
数学问题,自己动笔算吧
1433 0和5:
找规律问题,被9整除和5的个数是有关系的,算几组就可以了
1413 权势二进制:
找规律问题,这个很好想。就是所有位中的最大数字。
1432 独木舟:
很容易想的贪心,尽量将最重的和最轻的一起匹配,这样的浪费是最小的,自然是最优的。在对数据的顺序不做要求的时候,排序经常是一种有效的手段,能让问题简单不少。
1428 活动安排问题:
贪心+1 , 每次贪心的选择最先开始的活动,如果此时教室已经不够用,那么就需要再开一个。实际生活中也是这样的。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int Max = 1e6+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;int n, m;pair<ll, ll> arr[Max];ll heap[Max];bool cmp(pair<ll, ll> a, pair<ll ,ll> b){ if(a.first != b.first) return a.first<b.first; return a.second<b.second;}bool cmp1(int a, int b){ return a>b;}int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d", &n)) { int ans = 0, cnt = 0; heap[0] = llINF; for(int i=0; i<n; i++) scanf("%lld%lld", &arr[i].first, &arr[i].second); sort(arr, arr+n, cmp); for(int i=0; i<n; i++) { if(arr[i].first < heap[0]) { heap[cnt++] = arr[i].second; make_heap(heap, heap+cnt, cmp1); ans++; } else { heap[0] = arr[i].second; make_heap(heap, heap+cnt, cmp1); } } cout<<ans<<endl; } return 0;}
1417 天堂里的游戏:
好像也是一个找规律的题。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>using namespace std;typedef long long ll;const int Max = 1e6+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;int n, m;ll Gcd(ll a,ll b){ ll t = min(a, b); a = max(a, b); b = t; while( a%b ) { t = b; b = a%b; a = t; } return b;}int main( ){ //freopen("input.txt", "r", stdin); int T; cin>>T; while(T--) { ll a,b; scanf("%lld%lld", &a,&b); ll A = a+3*b; ll B = 4*(a+b); ll gcd = Gcd(A, B); cout<<A/gcd<<"/"<<B/gcd<<endl; } return 0;}
1315 合法整数集:
http://blog.csdn.net/xh413235699/article/details/72783711
1279 扔盘子
http://blog.csdn.net/xh413235699/article/details/72802196
1278 相离的圆
只需要将所有的圆的左位置进行记录,并进行排序,然后对于每一个圆我们都统计在他右边相离的圆的个数,只需要二分找到那个临界点就好了。而且这样统计下来并不会重复统计。因为我们是单方向统计的,很多时候单方向的计数都可以避免重复计数的问题。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>using namespace std;typedef long long ll;const int Max = 1e5+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;int n;int arr[Max], p[Max], r[Max];int main( ){ //freopen("input.txt", "r", stdin); while(cin>>n) { int ans = 0; for(int i=0; i<n; i++) { scanf("%d%d", p+i, r+i); arr[i] = p[i]-r[i]; } sort(arr, arr+n); for(int i=0; i<n; i++) { int k = upper_bound(arr, arr+n, p[i]+r[i])-arr;//刚好相离的那个圆的序号 ans += (n-k); } cout<<ans<<endl; } return 0;}
1266 蚂蚁:
最短时间很显然,主要是最长时间,这里就考验大家的分析能力了,仔细想想其实,那个碰头之后互相反向,其实可以等价为两个蚂蚁都是继续走自己的路,也就是说这个反向根本没用。是用来干扰我们理清问题的思路的。(可以这么想,两个蚂蚁碰头之后,他们处于同一个位置,也就是可以认为它们是同一个蚂蚁,那么这两个蚂蚁是没有区别的,只需要让每一个蚂蚁保持原来的行走方向就可以了,那么最大值就是max(x, l-x)).其实每次折返的过程可以看作两个蚂蚁互换了角色的过程,举个例子自己画画图比较好理解。
1138连续整数问题:
http://blog.csdn.net/xh413235699/article/details/72808736
1133 不重叠的线段:
贪心,优先选择左端点小的线段。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>using namespace std;typedef long long ll;const int Max = 1e5+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;struct xianduan{ int start, finish;};int n;xianduan arr[Max];bool cmp(const xianduan& a, const xianduan& b){ if(a.finish != b.finish) return a.finish<b.finish; return a.start < b.start;}int main( ){ //freopen("input.txt", "r", stdin); while(cin>>n) { for(int i=0; i<n; i++) scanf("%d%d", &arr[i].start, &arr[i].finish); sort(arr, arr+n, cmp); int ans = 0, currentend = -(1e9+5); for(int i=0; i<n; i++) { if(arr[i].start >= currentend) { ans++; currentend = arr[i].finish; } } cout<<ans<<endl; } return 0;}
1126 求递推序列的第n项:
转化递推公式为矩阵相乘的形式,这样可以利用矩阵的性质,先对矩阵的乘积进行计算,然后这里就可以使用矩阵快速幂算法O(logn)。算是一类型题吧,记下。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>using namespace std;typedef long long ll;const int Max = 1e5+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;struct matrix{ int arr[2][2]; matrix( ) { arr[0][0] = 1; arr[0][1] = 0; arr[1][0] = 0; arr[1][1] = 1; } void print( ) { for(int i=0; i<2; i++) { for(int j=0; j<2; j++) cout<<arr[i][j]<<" "; cout<<endl; } }};int n, a, b;void init(matrix& ans) //初始化矩阵{ ans.arr[0][0] = a; ans.arr[0][1] = b; ans.arr[1][0] = 1; ans.arr[1][1] = 0;}matrix cheng(const matrix& a, const matrix& b){ matrix answer; answer.arr[0][0] = (a.arr[0][0]*b.arr[0][0]+a.arr[0][1]*b.arr[1][0])%7; answer.arr[0][1] = (a.arr[0][0]*b.arr[0][1]+a.arr[0][1]*b.arr[1][1])%7; answer.arr[1][0] = (a.arr[1][0]*b.arr[0][0]+a.arr[1][1]*b.arr[1][0])%7; answer.arr[1][1] = (a.arr[1][0]*b.arr[0][1]+a.arr[1][1]*b.arr[1][1])%7; return answer;}void msp(matrix& ans, int n){ matrix temp;//储存快速幂遗留的乘积 while(n/2) { if(n%2) temp = cheng(temp, ans); ans = cheng(ans, ans); n /= 2; } ans = cheng(ans, temp);}int main( ){ //freopen("input.txt", "r", stdin); matrix ans; while(cin>>a>>b>>n) { if(n == 1 || n==2) { cout<<1<<endl; continue; } init(ans);//初始化矩阵,将递推公式转化为矩阵形式 msp(ans, n-2);//对矩阵进行快速幂运算O(logN)的复杂度 //ans.print( ); int t = (ans.arr[0][0]+ans.arr[0][1]+7)%7; cout<<t<<endl; } return 0;}
1119 机器人走方格:
http://blog.csdn.net/xh413235699/article/details/72859044
1095 Anigram单词:
互为Anigram的单词,出现的字母都是相同的,而且数量也相同。利用这点,对原始单词进行统计一遍,然后每一个单词按照字典序在排序一遍,最后查map,两个数量相减就是答案。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>#include<queue>using namespace std;typedef long long ll;const int Max = 1e4+5;const int mod = 1e9+7;//是素数const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;int m,n;int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d", &n)) { map<string, int> map1, map2; char s[11]; for(int i=0; i<n; i++) { scanf("%s", s); map1[s]++; int length = strlen(s); sort(s, s+length); map2[s]++; } scanf("%d", &m); while(m--) { char s[11]; scanf("%s", s); int ans1 = map1[s]; int length = strlen(s); sort(s, s+length); int ans2 = map2[s]; cout<<ans2 - ans1<<endl; } } return 0;}
1094 和为k的连续区间
没有什么复杂度的问题,就是一个map的灵活运用。(STL真的重要)
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>#include<queue>using namespace std;typedef long long ll;const int Max = 1e4+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;int n,k;ll arr[Max], sum[Max];int main( ){ //freopen("input.txt", "r", stdin); while(~scanf("%d%d",&n,&k)) { map<ll, bool> m; sum[0] = 0; for(int i=1; i<=n; i++) { scanf("%lld", arr+i); sum[i] = sum[i-1]+arr[i]; m[sum[i]] = true;//标记这个前缀和是可达的 } int flag = 1; for(int i=0; i<=n && flag; i++) if(m[sum[i]+k])//从i开始的前缀和与后面某一项的前缀和之差等于k for(int j=i; j<=n; j++) if(sum[j] == sum[i]+k) { flag = 0; cout<<i+1<<" "<<j<<endl; break; } if(flag) cout<<"No Solution"<<endl; m.clear( ); } return 0;}
1092 回文字符串:
总长度减去最大公共子序列就是答案。所以就是一个最大公共子序列问题。动态规划。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>#include<queue>using namespace std;typedef long long ll;const int maxn = 1005;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18;string in;int dp[maxn][maxn];int main( ){ //freopen("input.txt", "r", stdin); while(cin>>in) { string temp; int n = in.length(); for(int i=0; i<n; i++) temp.insert(i, 1, in[n-i-1]); //dp[i][j]表示以i,j下标结尾的字符串的最长公共子串 dp[0][0] = dp[1][0] = dp[0][1] = 0; for(int i=1; i<=n; i++) for(int j=1; j<=n; j++) { if(in[i-1] == temp[j-1]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = max(dp[i][j-1], dp[i-1][j]); } cout<<n-dp[n][n]<<endl; } return 0;}
1062 序列中最大的数:
预处理。
1067 Bash游戏:
找规律题,如果模7等于0或者2就是B赢,否则A赢。
1050 循环数组最大字段和:
http://blog.csdn.net/xh413235699/article/details/77837180
1042 数字0-9的数量:
http://blog.csdn.net/xh413235699/article/details/77886860
1031 骨牌覆盖:
找规律问题。好像是首项为1,2的斐波那契数列。
1007 正整数分组:
背包问题的变形。选择的部分和越接近sum/2则二者的差值就越小,也就是答案。只需要求解最接近sum/2的结果。
#include<stdio.h> #include<climits> #include<algorithm> #include<stack> #include<iostream> #include<cmath> #include<set> #include<vector> #include<map> #include<queue> #include<string.h> using namespace std; #define N 10010 int a[N]; int n; int dp[N]; int main(void) { while(scanf("%d",&n)!=EOF) { int sum=0; for(int i=1;i<=n;i++) { cin>>a[i]; sum+=a[i];//挑选出一些数字,是的越靠近sum/2,那么就是背包问题了 } memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { for(int j=sum/2;j>=a[i];j--) { dp[j]=max(dp[j],dp[j-a[i]]+a[i]); } } cout<<abs((sum-dp[sum/2])-dp[sum/2])<<endl; } return 0; }
1024 矩阵中不重复的元素:
取对数大法好,数据量完全可以枚举但是数据会溢出,这时候取对数就很有用了。
#include<iostream>#include<cstring>#include<math.h>#include<stdlib.h>#include<cstring>#include<cstdio>#include<utility>#include<algorithm>#include<map>#include<stack>#include<set>#include<queue>using namespace std;typedef long long ll;const int maxn = 1e6+5;const int mod = 1e9+7;const int Hash = 10000;const int INF = 1<<30;const ll llINF = 1e18+999;set<double> s;int main( ){ int a, b ,m ,n; while(cin>>m>>n>>a>>b) { for(int i=a; i<a+n; i++) for(int j=b; j<b+m; j++) { s.insert(1.0*j*log2(i)); } cout<<s.size()<<endl; }}
1014 X^2 Mod P:
水题,直接枚举就好。
1010 只包含因子2 3 5 的数:
http://blog.csdn.net/xh413235699/article/details/77898885
以上就是全部的51nod的二级算法题的题解了。总的来说虽然题不是很难,但是涉及的算法还是挺多的,常常回头看看,温故而知新。
其中大概涉及到:大数运算(这个有java的库可以用),二进制,dfs,数学问题(数学运算简化问题,除法取模,找规律),预处理,二分,贪心,矩阵快速幂,STL(stack,map,set),动态规划,取对数。
其中动态规划真的是很高深的东西,这下面还是可以分成好多类题。
算法者,贵在积累,深入浅出。
- 51nod二级算法题全部题解
- 51nod一级算法题全部题解
- 51 nod 1133 二级算法题 贪心
- 51Nod 1786 数据流中的算法-众数 题解
- [贪心]51Nod 1241 题解
- [51nod-1432]独木舟 题解
- 51nod 1007正整数分组 题解
- 51NOD 序列求和 V5题解
- [51nod-1128]正整数分组V2 题解
- [51nod-1068]Bash游戏 V3 题解
- [51nod-1428]活动安排问题 题解
- 51nod 1级算法题
- 51nod 2级算法题-1007
- 51nod 2级算法题-1010
- 51nod 2级算法题-1014
- 51nod 2级算法题-1050
- 51nod 2级算法题-1062
- 51nod 2级算法题-1092
- 代理模式proxy
- android存储路径
- java.lang.ClassNotFoundException: org.springframework.web.util.IntrospectorCleanupListener
- logback配置
- ubuntu14.04挂载新的硬盘
- 51nod二级算法题全部题解
- ubuntu tab键不能自动补全命令的参数
- 【数据结构----笔记5】插入排序算法之【希尔排序算法】
- laravel 服务容器service container和服务提供者service provider
- 再聊socket状态转移
- C++笔试题(二).CVTE
- 测试入门之测试阶段的划分
- 搭建网站过程中的小知识
- OpenGL