九度OJ 1396(DP) 1397(尺取法) 1398(最值) 1399(揹包,DP) 1401(未完成)


1396:最少零的路徑

題意

一個由非負整數構成的N * N大小的矩陣,你需要按照如下的規則找到一條訪問路徑:
1、起點爲該矩陣的最左上角元素
2、終點爲該矩陣的最右下角元素
3、在遍歷過程中,只允許從當前的單元移動到與之相鄰的右側單元或者下方單元

最後,當我們按照如上三個規則獲取路徑之後,我們會將所訪問到的單元中的數字相乘,同時希望得到的乘積末尾所含有的連續0的個數最少。找出這麼一條路徑,輸出其對應的末尾0的個數及遍歷的過程。其中往下走,則用D表示(Down);往右走,則使用R表示(Right),看Sample能幫助你理解。

思路

題意可以轉換爲這條路徑上的數字包含的2和5的因子個數的最小值最小。
那麼我們在DP過程中,將分別按照因子數2少以及因子數5少兩個原則來選取上一步路徑,最終得到的兩個路徑再取最小值就是答案。

代碼

#include <stdio.h>

#define N 1000

typedef struct node {
    int val;
    int c[2];
    char pre[2];
    int sum[2][2];
} Point;    

int count(int c, int val)
{           
    if (val == 0)
        return 1;
    int i = 0;
    while (val % c == 0)
    {       
        i ++;
        val /= c;
    }           
    return i;   
}                   

int cmp(int sum1[2], int sum2[2], int k)
{               
    if (sum1[k] != sum2[k])
        return (sum1[k] < sum2[k]) ? -1 : 1;
    else   
        return (sum1[1-k] < sum2[1-k]) ? -1 : 1;
}       

int min(int x, int y)
{
    return (x < y) ? x : y;
}

int n;
Point p[N+1][N+1];

int main(void)
{       
    int i, j, k;
    int haveZero, zi, zj;

    while (scanf("%d", &n) != EOF)
    {
        for (i=1; i<=n; i++)
        {
            for (k=0; k<2; k++)
            {
                p[i][0].sum[k][0] = p[i][0].sum[k][1] = 0;
                p[0][i].sum[k][0] = p[0][i].sum[k][1] = 0;
            }
        }
        haveZero = 0;
        for (i=1; i<=n; i++)
        {
            for (j=1; j<=n; j++)
            {
                scanf("%d", &p[i][j].val);
                if (p[i][j].val == 0)
                {
                    haveZero = 1;
                    zi = i;
                    zj = j;
                }
                p[i][j].c[0] = count(2, p[i][j].val);
                p[i][j].c[1] = count(5, p[i][j].val);
            }
        }
        /*
        for (i=1; i<=n; i++)
        {
            for (j=1; j<=n; j++)
            {
                printf("%d %d\t", p[i][j].c[0], p[i][j].c[1]);
            }
            printf("\n");
        }
        */

        for (i=1; i<=n; i++)
        {
            for (j=1; j<=n; j++)
            {
                for (k=0; k<2; k++)
                {
                    p[i][j].sum[k][0] = p[i][j].c[0];
                    p[i][j].sum[k][1] = p[i][j].c[1];
                    int tmp = cmp(p[i-1][j].sum[k], p[i][j-1].sum[k], k);
                    if ( j == 1 || (j != 1 && i != 1 && tmp < 0) )
                    {
                        p[i][j].pre[k] = 1;
                        p[i][j].sum[k][0] += p[i-1][j].sum[k][0];
                        p[i][j].sum[k][1] += p[i-1][j].sum[k][1];
                    }
                    else
                    {
                        p[i][j].pre[k] = 2;
                        p[i][j].sum[k][0] += p[i][j-1].sum[k][0];
                        p[i][j].sum[k][1] += p[i][j-1].sum[k][1];
                    }
                }
            }       
        }           
        /*          
        int r;  
        for (k=0; k<2; k++)
        {       
            for (r=0; r<2; r++)
            {
                for (i=1; i<=n; i++)
                {
                    for (j=1; j<=n; j++)
                    {
                        printf("%d ", p[i][j].sum[k][r]);
                    }
                    printf("\n");
                }
                printf("\n");
            }
        }
        */
        int m0 = min(p[n][n].sum[0][0], p[n][n].sum[0][1]);
        int m1 = min(p[n][n].sum[1][0], p[n][n].sum[1][1]);
        int m = min(m0, m1);
        if (haveZero && m >= 1)
        {
            printf("1\n");
            for (i=1; i<zi; i++)
                printf("D");
            for (j=1; j<zj; j++)
                printf("R");
            for (; i<n; i++)
                printf("D");
            for (; j<n; j++)
                printf("R");
            printf("\n");
            continue;
        }
        k = (m0 < m1) ? 0 : 1;
        char s[2*N];
        int sn = 0;
        i = n;
        j = n;
        while (i != 1 || j != 1)
        {
            int tmp = p[i][j].pre[k];
            if (tmp == 1)
            {
                i --;
                s[sn++] = 'D';
            }
            else
            {
                j --;
                s[sn++] = 'R';
            }
        }
        printf("%d\n", min(m0, m1));
        for (i=sn-1; i>=0; i--)
            printf("%c", s[i]);
        printf("\n");
    }

    return 0;
}
/**************************************************************
    Problem: 1396
    User: liangrx06
    Language: C
    Result: Accepted
    Time:460 ms
    Memory:32228 kb
****************************************************************/

1397:查找數段

題意

在BaiDu搜索引擎裏,如何提高搜索效率是研發人員爲之奮鬥的目標。現在,JOBDU密碼庫裏也有一段數字片段S(0<長度<=100,000),HQ想通過智能搜索得到包含關鍵字P(0<長度<=100,000)的某個數段長度,如果存在多個這樣的數段,則選擇長度最小的。例如,數字片段123456789,關鍵字爲257.顯然S本身就包含257,所以長度9是一個符合的數段,但是HQ從S中找到子串234567也包含關鍵字,並且無法找到更短的子串滿足條件,因此返回結果6。PS:JOBDU密碼庫裏的數字片段可能包含“*”,表示這一位可以是(0~9)中任意1個,具體見案例2。

思路

WA了好多次。整體思路是尺取法,但寫的時候還不知道這個概念,而且這個題需要注意很多特殊情況。

代碼

#include <stdio.h>
#include <string.h>

#define N 100000

int main(void)
{
    char s[N+1], mod[N+1];

    while (scanf("%s%s", s, mod) != EOF) {
        int num[200], need, n;
        n = strlen(s);
        memset(num, 0, sizeof(num));
        need = 0;
        for (int i = 0; mod[i]; i ++) {
            num[mod[i]] ++;
            need ++;
        }

        int l = 0, r = 0, any = 0;
        int ans = n+1;
        int cnt[200];
        memset(cnt, 0, sizeof(cnt));
        while (1) {
            while (r < n && any < need) {
                if (s[r] == '*') any++, r++;
                else {
                    if (cnt[s[r]] < num[s[r]])
                        need--;
                    cnt[s[r++]] ++;
                }
            }
            if (any < need) break;
            //printf("l=%d, r=%d\n", l, r);
            ans = (r-l < ans) ? (r-l) : ans;
            if (s[l] == '*') any--, l++;
            else {
                if (num[s[l]] && cnt[s[l]] <= num[s[l]])
                    need ++;
                cnt[s[l++]] --;
            }
        }
        printf("%d\n", ans%(n+1));
    }

    return 0;
}
/**************************************************************
    Problem: 1397
    User: liangrx06
    Language: C
    Result: Accepted
    Time:50 ms
    Memory:1040 kb
****************************************************************/

1398:移動次數

題意

衆所周知JOBDU旗下的JOBBALA公司是一家以個性、親民著稱的IT公司。在JOBBALA公司成立50週年的日子裏,公司CEO組織全體員工登山旅遊。按照往常的習慣,導遊通常要求遊客按照身高從低到高的順序排好,但是考慮這次JOBBALA人數太多,排序很耗時間。因此,導遊想了想,要求JOBBALA的員工可以隨便排,但是必須保證隊列的第一個是隊列中最矮的,隊列的最後一個是隊列中最高的。例如:隊列 { 1, 4, 3, 2, 2, 5} 就是符合的隊列,{1, 4, 2, 3, 2, 5}也符合,而{2, 1, 2, 3, 4, 5}就是錯的。請問對於任意的隊列,最少要兩兩交換多少次,可以讓其符合導遊的要求?

思路

最靠左的那個最矮的交換到最左側,然後最靠右的那個最高的交換到最右側即可。

代碼

#include <stdio.h>
#include <limits.h>

#define N 200

int main(void)
{
    int n, i;
    int a[N];
    int maxi, mini;
    int max, min;

    while (scanf("%d", &n) != EOF)
    {
        min = INT_MAX;
        for(i=0; i<n; i++)
        {
            scanf("%d", &a[i]);
            if (a[i] < min)
            {
                min = a[i];
                mini = i;
            }
        }
        max = 0;
        for(i=n-1; i>=0; i--)
        {
            if (a[i] > max)
            {
                max = a[i];
                maxi = i;
            }
        }

        int res = mini + n-1-maxi;
        if (mini > maxi)
            res --;
        printf("%d\n", res);
    }

    return 0;
}
/**************************************************************
    Problem: 1398
    User: liangrx06
    Language: C
    Result: Accepted
    Time:60 ms
    Memory:912 kb
****************************************************************/

1399:名偵探柯南

題意

一個貴族的家裏被盜。這個貴族的家裏非常有錢,但這家主人的習慣很怪異,他將所有的金銀珠寶都磨成粉裝到幾個分開的袋子裏。由於之前並沒有記錄,所以主人並不知道這次被盜自己損失了多少錢。幾天後,盜竊犯被抓住,但是他身上僅有一個盜竊時用的包,盜竊走的財產早已經揮霍一空。很顯然,盜竊犯一定會使自己偷走的東西的總價值最大,柯南雖然斷案如神,但是他卻無法計算出盜竊犯到底盜走了價值多少錢的東西。你能幫幫柯南嗎?

思路

不要求是整數的那種揹包,貪心法求解即可,優先選取價值重量比最高的。

代碼

#include <stdio.h>
#include <math.h>
#include <stdlib.h>

#define N 100000

typedef struct node {
    double w;
    double v;
    double r;
} Jew;

int cmp(const void *a, const void *b)
{
    //Jew *x = (Jew *)a;
    //Jew *y = (Jew *)b;
    //long long k = x->v * y->w;
    //long long m = y->v * x->w;
    //return k > m;
    return (((Jew *)a)->r > ((Jew *)b)->r) ? -1 : 1;
}

int main(void)
{
    int n, i;
    double c, v;
    Jew j[N];

    while (scanf("%d%lf", &n, &c) != EOF)
    {
        for (i=0; i<n; i++)
        {
            scanf("%lf%lf", &j[i].w, &j[i].v);
            j[i].r = (j[i].v)/(j[i].w);
        }
        qsort(j, n, sizeof(j[0]), cmp);

        v = 0;
        for (i=0; i<n; i++)
        {

            if (c <= j[i].w)
            {
                v += c * j[i].r;
                break;
            }
            else
            {
                v += j[i].v;
                c -= j[i].w;
            }
        }
        printf("%d\n", (int)round(v));
    }

    return 0;
}
/**************************************************************
    Problem: 1399
    User: liangrx06
    Language: C
    Result: Accepted
    Time:650 ms
    Memory:5536 kb
****************************************************************/

1401:阿里巴巴和N個強盜

題意

好吧,我又賣關子了,今天的重點是N個強盜之間的故事。
水滸傳中的108個好漢(及好女子)會有一個排名,N個強盜之間也要排出一個名次來。不可否認,有許多盜竊團伙很專業,其中也不乏嚴格的規矩。但這N個強盜是個無組織、無紀律的團伙,他們之間的排序完全是根據一場決鬥來定的,比如在一場決鬥中,強盜A戰勝了強盜B,那麼就說強盜A的排名高於強盜B。決鬥是隨機發生的,也就是哪天兩個強盜互相看不慣對方了,那麼就開始一場決鬥。而這樣產生的結果就是,有可能拿出兩個強盜來,我們根本就不知道這兩個強盜到底誰更厲害一些。這沒有關係,今天你只要給出能根據我給你的信息來判斷出某兩個強盜誰的排名更靠前一些就行。
你需要注意的是,強盜之間的排名有傳遞性,比如強盜A的排名比強盜B靠前,而強盜B的排名又比強盜C的靠前,那麼我們就認爲強盜A的排名要比強盜C的靠前。
現在,我能給你的信息是強盜之間發生決鬥的次數,以及每次參加決鬥的兩名強盜的姓名及最後決鬥的結果。

思路

這個題顯然應該用拓撲排序來做,但還加了很多規則,目前還沒有AC。

代碼

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章