1. 程式人生 > >遞迴+暴解+貪心

遞迴+暴解+貪心

八成都是水題。

A題猴子吃桃太水。

B題瘋狂的母牛,分別用三個變數儲存一年,二年和三年齡的牛。第四年時三年齡的牛就要生小牛了。 注意一點就是,第一年只有一頭三年齡的母牛。

C題 2的n-k次方

D題 貪心,把線段按照起點的從小到大排序,起點一樣按終點小的排在前面。然後迴圈一邊,每次取相鄰兩個線段的重疊長度,和與之前已經得到的重疊長度的最大值。

#include <stdio.h>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 50000+10;
struct node{
    LL x1, x2;
};
struct node p[MAXN];
bool cmp(struct node p1, struct node p2)
{
    if( p1.x1 == p2.x1 )
        return p1.x2 < p2.x2;
    return p1.x1 < p2.x1;
}
int main()
{
    int n;
    while( cin >> n )
    {
        for( int i=0; i<n; i++ )
            scanf("%lld %lld", &p[i].x1, &p[i].x2 );
        sort(p, p+n, cmp);
        LL ans = 0;
        struct node temp = p[0];    // 表示前一條線段
        for( int i=1; i<n; i++ )
        {
            if( p[i].x2 <= temp.x2)   //線段i線上段i-1內
            {
                ans = max(ans, p[i].x2 - p[i].x1);
            }
            else if(p[i].x1 <= temp.x2 && p[i].x2 >= temp.x2)
            {
                ans = max(ans, temp.x2 - p[i].x1);
                temp = p[i];           //選擇最靠後的線段
            }
        }
        cout<< ans <<endl;
    }
    return 0;
}

我也有一點不夠明白,為什麼每次要都取的是相鄰的兩個線段呢,舉個例子

現在有五條線段用 s0,s1,s2,s3,s4來表示
編號    起點x1  終點x2
s0      1       5
s1      2       4
s2      2       6
s3      4       9
s4      7       9
那麼我們可以看到最長的重疊度應該是 s0 與 s2 重疊長度是 5-2 = 3 。
那麼如果按AC程式碼來算的話
首先一個 int ans = 0;
然後是一個 struct node temp = s0;
迴圈是 for( int i=1; i<5; i++ )
之後判斷如果是後一條線段是否完全在前一條線段之內
如果是  ans = max(ans, s[i].x2 - s[i].x1);
如果不是 ans = max(ans, temp.x2 - s[i].x1);
然後temp = s[i];
那麼ans的值得變化是
起始:             ans = 0;
i = 1, temp = s0   ans = max(0, 4-2) = 2;
i = 2, temp = s1   ans = max(2, 4-2) = 2;
i = 3, temp = s2   ans = max(2, 6-4) = 2;
i = 4, temp = s3   ans = max(2, 9-7) = 2;
最終的結果就是 ans = 2;
但是我們明顯知道的是 最長的重疊是s0與s2的重疊長度 5 - 2 = 3;
即最終的結果應該是3才對。

也很可能是我太菜,想不明白,還請留言幫忙指出錯誤

我剛寫這道題的時候就考慮到這一點了但是寫出的程式碼提交在第十二個test就錯了,

我的想法就是把每一條線段和它之後的有重疊的線段,把他們的重疊長度都求出來,然後用一個動態陣列儲存,最後用sort排序一下,那麼陣列中最後一個數就是最大的重疊長度。

#include <stdio.h>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MAXN = 60000+10;
struct node{
    LL x1, x2;
};
struct node p[MAXN];
bool cmp( struct node p1, struct node p2 )
{
    if( p1.x1 == p2.x1 )
        return p1.x2 < p2.x2;
    return p1.x1 < p2.x1;
}
vector<LL> ve;
int main()
{
    int n;
    int ans = 0;
    while( ~scanf("%d", &n ) )
    {
        ve.clear();
        for( int i=0; i<n; i++ )
            scanf("%lld %lld", &p[i].x1, &p[i].x2 );
        sort(p, p+n, cmp);
        LL temp = 0;
        for( int i=0; i<n; i++ )
        {
            for( int j=i+1; i<n; j++ )
            {
                if( p[i].x2 >= p[j].x2 )
                {
                    temp = p[j].x2 - p[j].x1;
                    ve.push_back(temp);
                }
                else if( p[i].x2 >= p[j].x1 && p[i].x2 < p[j].x1 )
                {
                    temp = p[i].x2 - p[j].x1;
                    ve.push_back(temp);
                }
                else
                    break;
            }
        }
        sort(ve.begin(), ve.end());
        if( ve.empty() )
            printf("0\n");
        else
            printf("%lld\n", ve[ve.size()-1] );
    }

    return 0;
}

E題 貪心,先從大到小排序,每次最大的和最小的能一起走就一起走,不能就大的自己走。

F題 用了一個很巧妙的方法,直接看程式碼,很易懂

#include <bits/stdc++.h>
using namespace std;
const int  MAXN = 1000+10;
int num[MAXN];

int main()
{
    int n;
    while( scanf("%d", &n) != EOF )
    {
        for( int i=0; i<n; i++ )
            scanf("%d", &num[i] );
        int ans = 0;
        if( num[0] == 0 )
            ans ++;
        for( int i=1; i<n; i++ )
        {
            switch (num[i])
            {
                case 0:
                    {
                        ans++;
                        break;
                    }
                case 1:
                    {
                        if( num[i-1] == 1 )  
                            ans ++, num[i] = 0;
                        break;
                    }
                case 2:
                    {
                        if( num[i-1] == 2 )  
                            ans ++, num[i] = 0 ;
                        break;
                    }
                case 3:
                    {
                        if(num[i-1] == 1)  
                            num[i] = 2;
                        else if(num[i-1] == 2 ) 
                            num[i] = 1;
                        break;
                    }
            }
        }
        printf("%d\n", ans );
    }

    return 0;
}

G題的方法有些想不到。先求前一個的gcd並存到陣列,然後是前兩個,前三個,一直到最後一個。 然後逆序的再把這個數列的gcd求一遍,並放入一個新的陣列。然後取正序求得gcd中的倒數第二個,倒序中正數第二個兩者的最大值。這樣得到的是去掉數列中第一個或者最後一個剩下的數的最大公約數。然後開一個迴圈,具體看程式碼,這個迴圈求得每次都是除掉數列兩頭的數,每次漏掉中間的一個數的最大公約數。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100000+10;

int num[MAXN];
int vb[MAXN];
int ve[MAXN];
int gcd(int a, int b)
{
    return b ? gcd(b, a%b) : a ;
}

int main()
{
    int t;
    cin >> t;
    while( t-- )
    {
        int n;
        scanf("%d", &n );
        for( int i=0; i<n; i++ )
            scanf("%d", &num[i]);
        vb[0] = num[0];
        for( int i=1; i<n; i++ )
        {
            vb[i] = gcd( num[i], vb[i-1] );

        }
        ve[n-1] = num[n-1];
        for( int i = n-2; i>=0; i-- )
        {
            ve[i] = gcd(num[i], ve[i+1]);
           // ve[i] = temp;
        }
        int ans = max(vb[n-2], ve[1]);
        for( int i=1; i<n-1; i++ )
        {
            ans = max(ans, gcd(vb[i-1], ve[i+1]));
        }
        printf("%d\n", ans );

    }

    return 0;
}

H題和 I 題都是直接暴力遍歷一遍就行了,注意可能爆 int

J題是2011年的ACM亞洲區域賽的一道題,具體見杭電4007題

這道題在百度上搜索一下,解法有很多種,這裡只說一下我認為最簡單的一種,其實也是百度了很多種解法,比較得到的

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1000+10;

struct node {
    int x, y;
};
struct node p[MAXN];
int arrx[MAXN];    // 便於儲存篩選出的x座標
int arry[MAXN];    // 便於儲存篩選出的y座標
bool cmp( int a, int b )
{
    return a < b;
}

int main()
{
    int n, r;
    while( cin>> n >> r )
    {
        for( int i=0; i<n; i++ )
        {
            scanf("%d %d", &p[i].x, &p[i].y );
            arry[i] = p[i].y;
        }
        sort(arry, arry + n, cmp);
        int ans = 0;
        for( int i=0; i<n; i++ )
        {
            int temp = arry[i];    // 搜尋的正方形的下邊
            int cntx = 0;   // 符合條件的x的個數
            for( int j=0; j<n; j++ )
            {
                if( p[j].y >= temp && p[j].y <= temp + r )
                    arrx[cntx++] = p[j].x;  // 篩選出符合縱座標上條件的x
            }
            sort(arrx, arrx + cntx, cmp);

            int edx = 0;
            for( int j=0; j<n; j++ )
            {                      // arrx[edx] - arrx[j]得到的是當前兩點的水平距離
                while( edx < cntx && arrx[edx] - arrx[j] <= r )
                    edx ++;
                ans = max(ans, edx- j);
            }
        }
        printf("%d\n", ans );
    }

    return 0;
}

大概就是先用一個數組儲存每個點的y座標,然後用這個陣列篩選出符合條件的x的座標,然後在用這些符合條件的點來遍歷一下,最多能有多少個點符合條件。這裡說一下變數edx是什麼,首先edx表示的已經找到的符合條件的點數,但是它定義在j的外面,而  arr[j] 表示的是邊長為 r 的正方形的左邊界,所以當然最後edx減去 j 才得到的是當前矩形位置有多少個點符合條件。