九度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。

代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值