九度OJ 1407(线段树) 1408(DP) 1409(DP) 1410(DP) 1411(最短路)


1407:快速找出最小数

题意

给定一个大小为N 的整数数组array,我们定义两种操作:
1) Add(L, R, W)。即将子数组[L, R]中的元素,都累加一个整数W。
2) Min(L, R)。即返回子数组[L, R]之中,最小的一个元素的值。
其中L和R为数组的下标,且从0开始计数。当数组下标L > R时,我们认为这个子数组的元素包含array[L], array[L+1], … array[N – 1], array[0], array[1], … array[R]。

思路

区间查询最小值问题,一般用线段树来解决,对于要求不高的,还可以用分桶法(也就是平方分割)。

代码

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

#define N 100000
#define M 100
#define INF INT_MAX

int a[N];
int seg[4*N];
int add[4*N];

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

void pullup(int i)
{
    seg[i] = min(seg[2*i], seg[2*i+1]);
}

void pushdown(int i)
{
    if (add[i])
    {
        add[2*i] += add[i];
        add[2*i+1] += add[i];
        seg[2*i] += add[i];
        seg[2*i+1] += add[i];
        add[i] = 0;
    }
}

void create(int i, int b, int e)
{
    add[i] = 0;
    if (b == e)
    {
        seg[i] = a[b];
        return;
    }
    create(2*i, b, (b+e)/2);
    create(2*i+1, (b+e)/2+1, e);
    pullup(i);
}

void update(int w, int i, int b, int e, int l, int r)
{
    //printf("%d %d %d %d %d\n", i, b, e, l, r);
    //printf("=====");
    //for (int j=0; j<11; j++)
    //  printf("%d ", seg[j]);
    //printf("\n");
    //for (int j=0; j<11; j++)
    //  printf("%d ", add[j]);
    //printf("\n");
    if (b > r || e < l)
        return;
    if (l <= b && e <= r)
    {
        add[i] += w;
        seg[i] += w;
        return;
    }
    pushdown(i);
    update(w, 2*i, b, (b+e)/2, l, r);
    update(w, 2*i+1, (b+e)/2+1, e, l, r);
    pullup(i);
}

int getMin(int i, int b, int e, int l, int r)
{
    if (b > r || e < l)
        return INF;
    if (l <= b && e <= r)
    {
        return seg[i];
    }
    pushdown(i);
    int m1 = getMin(2*i, b, (b+e)/2, l, r);
    int m2 = getMin(2*i+1, (b+e)/2+1, e, l, r);
    //pullup(i);
    return min(m1, m2);
}

int main(void)
{
    int n, i, m;
    char s[M];
    int l, r, w;

    while (scanf("%d", &n) != EOF)
    {
        for (i=0; i<n; i++)
            scanf("%d", &a[i]);
        create(1, 0, n-1);

        scanf("%d", &m);
        getchar();
        for (i=0; i<m; i++)
        {
            scanf("%d%d", &l, &r);
            gets(s);
            if (s[0] == '\0')
            {
                if (l > r)
                {
                    int m1 = getMin(1, 0, n-1, l, n-1);
                    int m2 = getMin(1, 0, n-1, 0, r);
                    printf("%d\n", min(m1, m2));
                }
                else
                    printf("%d\n", getMin(1, 0, n-1, l, r));
            } 
            else
            {
                sscanf(s, "%d", &w);
                if (l > r)
                {
                    update(w, 1, 0, n-1, l, n-1);
                    update(w, 1, 0, n-1, 0, r);
                }
                else
                    update(w, 1, 0, n-1, l, r);
            }
        }
    }

    return 0;
}   
/**************************************************************
    Problem: 1407
    User: liangrx06
    Language: C
    Result: Accepted
    Time:360 ms
    Memory:4432 kb
****************************************************************/

1408:吃豆机器人

题意

淘宝公司内部有许多新鲜的小玩具,例如淘宝智能机器人。小时候,大家都玩过那个吃豆子的游戏吧,这机器人就是按照这个游戏设计的,它会朝着豆子的方向行走。不过机器人还存在一个bug,他只会朝南和朝东走。现在有一块空地,分成了n*m个格子,每个格子内有一颗豆子。机器人的起点在西北角,终点在东南角。请问机器人从起点到终点有多少种不同的方法。

思路

简单的DP。但其实也可以直接用组合数公式来给出答案,但是n和m太大,目前还没有掌握这种大组合数取模的计算方法。

代码

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

#define N 1000
#define M 10009

int main(void)
{
    int n, m, i, j;
    int a[N+1][N+1];

    memset(a, 0, sizeof(a));
    for (i=1; i<=1000; ++i)
        a[1][i] = a[i][1] = 1;
    for(i=2; i<=N; i++)
    {
        for (j=2; j<=N; j++)
        {
            a[i][j] = (a[i-1][j]+a[i][j-1]) % M;
        }
    }

    while (scanf("%d%d", &n, &m) != EOF)
    {
        printf("%d\n", a[n][m]);
    }

    return 0;
}
/**************************************************************
    Problem: 1408
    User: liangrx06
    Language: C
    Result: Accepted
    Time:20 ms
    Memory:4752 kb
****************************************************************/

1409:TBString

题意

淘宝公司内部有一个字符串小王子,他平常无聊就研究字符串。一天,他在研究字符串TBTBBT时,他定义了一个统计函数F,F(S)表示一个字符串当中S出现的次数。对于字符串TBTBBT,那么就有F(T)=3,F(B)=3,F(TB)=2,F(BT)=2。但如果我们已知F(T),F(B),F(TB)和F(BT)这四个值,你能求出满足这4个条件,同时字典序最小的字符串么?若存在,则输出这个字符串;若不存在,则输出-1。
值得注意的是,字符串小王子认为T是比B小的,因为如果B比T小,那么字符串开头可能就会是BTTB,哈哈,你们邪恶了吧。

思路

这个题应该属于DP,思路上并不难,但真的好麻烦,需要注意各种可能的情况。所幸竟然一遍AC了,不然真是错了都不想查。

代码

#include <stdio.h>

int main()
{
    int i;
    int t, b, tb, bt;
    char s[2000001];

    while (scanf("%d%d%d%d", &t, &b, &tb, &bt) != EOF) {
        int flag = 1;
        if (tb == bt) {
            if (t < tb || b < tb)
                flag = 0;
            else if (t == tb && b == tb)
                flag = 0;
            else if (t == tb) {
                int k = 0;
                for (i = 0; i < t; i ++)
                    s[k++] = 'B', s[k++] = 'T';
                for (i = 0; i < b-tb; i ++)
                    s[k++] = 'B';
                s[k] = '\0';
            }
            else {
                int k = 0;
                for (i = 0; i < t-tb; i ++)
                    s[k++] = 'T';
                for (i = 0; i < tb; i ++) {
                    s[k++] = 'B';
                    if (i == tb-1) {
                        for (int j = 0; j < b-tb; j ++)
                            s[k++] = 'B';
                    }
                    s[k++] = 'T';
                }
                s[k] = '\0';
            }
        }
        else if (tb > bt) {
            if (tb != bt+1 || t < tb || b < tb)
                flag = 0;
            else {
                int k = 0;
                for (i = 0; i < t-tb; i ++)
                    s[k++] = 'T';
                for (i = 0; i < tb; i ++)
                    s[k++] = 'T', s[k++] = 'B';
                for (i = 0; i < b-tb; i ++)
                    s[k++] = 'B';
                s[k++] = '\0';
            }
        }
        else {
            if (tb != bt-1 || t < bt || b < bt)
                flag = 0;
            else {
                int k = 0;
                for (i = 0; i < bt; i ++) {
                    s[k++] = 'B';
                    if (i == bt-1) {
                        for (int j = 0; j < b-bt; j ++)
                            s[k++] = 'B';
                    }
                    s[k++] = 'T';
                    if (i == 0) {
                        for (int j = 0; j < t-bt; j ++)
                            s[k++] = 'T';
                    }
                }
                s[k] = '\0';
            }
        }
        if (flag == 0)
            puts("-1");
        else
            puts(s);
    }

    return 0;
}
/**************************************************************
    Problem: 1409
    User: liangrx06
    Language: C
    Result: Accepted
    Time:90 ms
    Memory:2792 kb
****************************************************************/

1410:垒积木

题意

给你一些长方体的积木,问按以下规则能最多垒几个积木。
1 一个积木上面最多只能垒另一个积木。
2 在下面的积木的长宽高要大于或等于上面的积木的长宽高

思路

开始没有想到用DP,没有好的思路。看到别人用DP才发现好简单。

代码

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

#define N 1000000
#define M 100

int max(int a, int b)
{
    return (a>b) ? a : b;
}

int dp[M+1][M+1][M+1];
int count[M+1][M+1][M+1];

int main(void)
{
    int n, i, j, k, r, m;
    int len, wid, heig;

    while (scanf("%d", &n) != EOF)
    {
        memset(count, 0, sizeof(count));
        for (i=0; i<n; i++)
        {
            scanf("%d%d%d", &len, &wid, &heig);
            count[len][wid][heig] ++;
        }

        memset(dp, 0, sizeof(dp));
        m = 0;
        for (j=1; j<=M; j++)
        {
            for (k=1; k<=M; k++)
            {
                for (r=1; r<=M; r++)
                {
                    dp[j][k][r] = max(max(dp[j-1][k][r], dp[j][k-1][r]),
                                dp[j][k][r-1]) + count[j][k][r];
                    m = max(m, dp[j][k][r]);
                }
            }
        }
        printf("%d\n", m);
    }

    return 0;
}
/**************************************************************
    Problem: 1410
    User: liangrx06
    Language: C
    Result: Accepted
    Time:1210 ms
    Memory:8964 kb
****************************************************************/

1411:转圈

题意

在一个有向图有n个顶点(编号从1到n),给一个起点s,问从起点出发,至少经过一条边,回到起点的最短距离。

思路

主体用dijkstra算法求解,但题目要求至少经过一条边,所以应该将起点直接相连的所有边初始化赋值距离,其它的(包括起点)赋值无穷大。

代码

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

#define INF 100000000
#define N 501

int main()
{
    int n, m, s, a, b, c, map[N][N], len[N], used[N];
    while (scanf("%d%d%d", &n, &m, &s) != EOF)
    {
        memset(map, 0, sizeof(map));
        while (m--)
        {
            scanf("%d%d%d", &a, &b, &c);
            if (map[a][b]==0 || map[a][b]>c)
                map[a][b] = c;
        }

        for(int i=1; i<=n; i++)
            if(map[s][i] != 0)
                len[i] = map[s][i];
            else
                len[i] = INF;
        int index, min;
        memset(used, 0, sizeof(used));
        while(1)
        {
            min = INF, index = -1;
            for(int i=1; i<=n; i++)
                if(!used[i] && len[i] < min)
                    min = len[i], index = i;
            if(index == -1 || index == s)
                break;
            used[index] = 1;
            for(int i = 1;i <= n; i++)
                if(!used[i] && map[index][i] && len[i] > len[index] + map[index][i])
                    len[i] = len[index] + map[index][i];
        }
        if(index == -1)
            printf("help!\n");
        else
            printf("%d\n", len[s]);
    }
}
/**************************************************************
    Problem: 1411
    User: liangrx06
    Language: C
    Result: Accepted
    Time:120 ms
    Memory:1828 kb
****************************************************************/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值