您的位置:首页 > 其它

51nod二级算法题全部题解

2017-09-11 10:18 337 查看
首先对二级算法做一个总结吧,大体上都是一些不是很难的题,但是很有助于提高的,毕竟对于我这样的萌新来说。既然题已经刷完了,当然还是写一篇总结,对学到的经验进行一下总结。。。不然下次碰到不会的还是不会就很尴尬了。
下面进入正文:
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));
}
}

1596 搬货物问题:二进制的重要性,存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;
}

1521 一维战舰:核心就是每次失去一个可用位置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
4000
<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

<<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
;
int n;
int dp
;
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),动态规划,取对数。
其中动态规划真的是很高深的东西,这下面还是可以分成好多类题。

算法者,贵在积累,深入浅出。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: