51nod二级算法题全部题解

首先对二级算法做一个总结吧,大体上都是一些不是很难的题,但是很有助于提高的,毕竟对于我这样的萌新来说。既然题已经刷完了,当然还是写一篇总结,对学到的经验进行一下总结。。。不然下次碰到不会的还是不会就很尴尬了。

下面进入正文:

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


1596 搬货物问题:

二进制的重要性,存0-1e6的这些指数的出现次数,然后不停的除2进位就可以,从低位开始,只要是留下的某一位进不上去剩下一个了,那就是一定要花费一次机会去运送这个。需要注意的是:1,题中输入的货物的2^wi的wi不是货物的重量;2,数组最大要开大点,后面可能会进好几次超过1e6.

#include
#include
#include
#include
#include
#include
#include
#include
#include
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


1521 一维战舰:

核心就是每次失去一个可用位置k,对于整个区间的影响。也就是只需要计算出来k位于的区间[l, r]本来的可容纳战舰数再减去k被占据之后,可容纳的战舰数。有点像莫对算法里的区间转移的问题。解决了这个就按题意模拟就行了

#include
#include
#include
#include
#include
#include
#include
#include
#include
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


1489 蜥蜴和地下室:

dfs,这个没啥好说的,就是多练练吧,数据量很小,dfs就好了

#include
#include
#include
#include
#include
#include
#include
#include
#include
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

1629 B君的圆锥:

数学问题,自己动笔算吧


1433 0和5:

找规律问题,被9整除和5的个数是有关系的,算几组就可以了


1413 权势二进制:

找规律问题,这个很好想。就是所有位中的最大数字。


1432 独木舟:

很容易想的贪心,尽量将最重的和最轻的一起匹配,这样的浪费是最小的,自然是最优的。在对数据的顺序不做要求的时候,排序经常是一种有效的手段,能让问题简单不少。


1428 活动安排问题:

贪心+1 , 每次贪心的选择最先开始的活动,如果此时教室已经不够用,那么就需要再开一个。实际生活中也是这样的。

#include
#include
#include
#include
#include
#include
#include
#include
#include
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 arr[Max];
ll heap[Max];
bool cmp(pair a, pair b)
{
    if(a.first != b.first)
        return a.firstb;
}
int main( )
{
    //freopen("input.txt", "r", stdin);
    while(~scanf("%d", &n))
    {
        int ans = 0, cnt = 0;
        heap[0] = llINF;
        for(int i=0; i


1417 天堂里的游戏:

好像也是一个找规律的题。

#include
#include
#include
#include
#include
#include
#include
#include
#include
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<

1315 合法整数集:

http://blog.csdn.net/xh413235699/article/details/72783711


1279 扔盘子

http://blog.csdn.net/xh413235699/article/details/72802196


1278 相离的圆

只需要将所有的圆的左位置进行记录,并进行排序,然后对于每一个圆我们都统计在他右边相离的圆的个数,只需要二分找到那个临界点就好了。而且这样统计下来并不会重复统计。因为我们是单方向统计的,很多时候单方向的计数都可以避免重复计数的问题。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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


1266 蚂蚁:

最短时间很显然,主要是最长时间,这里就考验大家的分析能力了,仔细想想其实,那个碰头之后互相反向,其实可以等价为两个蚂蚁都是继续走自己的路,也就是说这个反向根本没用。是用来干扰我们理清问题的思路的。(可以这么想,两个蚂蚁碰头之后,他们处于同一个位置,也就是可以认为它们是同一个蚂蚁,那么这两个蚂蚁是没有区别的,只需要让每一个蚂蚁保持原来的行走方向就可以了,那么最大值就是max(x, l-x)).其实每次折返的过程可以看作两个蚂蚁互换了角色的过程,举个例子自己画画图比较好理解。


1138连续整数问题:

http://blog.csdn.net/xh413235699/article/details/72808736


1133 不重叠的线段:

贪心,优先选择左端点小的线段。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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>n)
    {
        for(int i=0; i= currentend)
            {
                ans++;
                currentend = arr[i].finish;
            }
        }
        cout<

1126 求递推序列的第n项:

转化递推公式为矩阵相乘的形式,这样可以利用矩阵的性质,先对矩阵的乘积进行计算,然后这里就可以使用矩阵快速幂算法O(logn)。算是一类型题吧,记下。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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<>a>>b>>n)
    {
        if(n == 1 || n==2)
        {
            cout<<1<

1119 机器人走方格:

http://blog.csdn.net/xh413235699/article/details/72859044


1095 Anigram单词:

互为Anigram的单词,出现的字母都是相同的,而且数量也相同。利用这点,对原始单词进行统计一遍,然后每一个单词按照字典序在排序一遍,最后查map,两个数量相减就是答案。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 map1, map2;
        char s[11];
        for(int i=0; i

1094 和为k的连续区间

没有什么复杂度的问题,就是一个map的灵活运用。(STL真的重要)

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 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<

1092 回文字符串:

总长度减去最大公共子序列就是答案。所以就是一个最大公共子序列问题。动态规划。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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

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  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
#include  
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<

1024 矩阵中不重复的元素:

取对数大法好,数据量完全可以枚举但是数据会溢出,这时候取对数就很有用了。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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 s;
int main( )
{
    int a, b ,m ,n;
    while(cin>>m>>n>>a>>b)
    {
        for(int i=a; i

1014 X^2 Mod P:

水题,直接枚举就好。


1010 只包含因子2 3 5 的数:

http://blog.csdn.net/xh413235699/article/details/77898885


以上就是全部的51nod的二级算法题的题解了。总的来说虽然题不是很难,但是涉及的算法还是挺多的,常常回头看看,温故而知新。

其中大概涉及到:大数运算(这个有java的库可以用),二进制,dfs,数学问题(数学运算简化问题,除法取模,找规律),预处理,二分,贪心,矩阵快速幂,STL(stack,map,set),动态规划,取对数。

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


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

你可能感兴趣的:(ACM)