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));    }}


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<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),动态规划,取对数。

其中动态规划真的是很高深的东西,这下面还是可以分成好多类题。


算法者,贵在积累,深入浅出。