zoukankan      html  css  js  c++  java
  • ACM学习历程—SNNUOJ1213 加油站问题(动态规划 || 数学)

    题目链接:http://219.244.176.199/JudgeOnline/problem.php?id=1213

    这是这次微软实习面试的一道题,当时只相出了一个2n的做法,面试官让我优化成n的做法。不过当时没想出来,最后面试官提示到了最后才恍然大悟。

    题目大意就是在一个环上有n个加油站,已知第i个加油站可以提供ai的油量,从第i个加油站到第i+1个加油站需要消耗bi的油量。(当i为n时,则表示n到1的油量)

    问:是否能从某个加油站出发,绕环一圈回到这个加油站?(当途中到达某个加油站时的油量小于0则不可)。

    首先可以设f(i) = a(i)-b(i),然后就可以转换成,在某个点出发回到这个点的过程中fsum值恒大于等于0。然后就联系到最大子序列和,如果从最大子序列的起点处出发都不能绕一圈,那么显然任何位置都不行,因为任何位置出发经过最大子序列时,必然sum值小于最大子序列和,这样的话后面必然某处会出现小于0的情况。不然的话,这个位置就能构成更大的子序列,就矛盾了。

    于是,我需要计算出环上的最大子序列,我想到的方法便是倍长一下,然后再求,于是就变成了2n的做法。代码中可以稍微优化一下就不需要f数组了。

    关于O(n)的做法,其实也挺好想的,不过当时面试的时候,一紧张就思维混乱了。

    首先如果从某个i出发,到j的时候sum(i, j)小于0了。那么[i ,j]间所有的点k出发到j处的sum(k, j)都会小于0,因为i能到k,自然sum(i, k)大于等于0,而sum(i, j) = sum(i, k-1)+sum(k, j),必然sum(k, j)要更小一点。那么[i, j]间的点都可以排除了。于是可以直接从0点出发绕一圈,遇到i不行,就继续从i+1出发,直到能跑完或者跑到n的位置处。

    代码(n的做法)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <queue>
    #include <vector>
    #include <string>
    #define LL long long
    
    using namespace std;
    
    const int maxN = 100005;
    int n, a[maxN], b[maxN];
    
    void input()
    {
        for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &b[i]);
    }
    
    void work()
    {
        int rest;
        bool flag;
        for (int i = 0; i < n; ++i)
        {
            flag = true;
            rest = 0;
            int j;
            for (j = 0; j < n; ++j)
            {
                if (rest < 0)
                {
                    flag = false;
                    break;
                }
                rest += a[(i+j)%n]-b[(i+j)%n];
            }
            if (flag && rest >= 0)
            {
                printf("Yes
    ");
                return;
            }
            else i = min(i+j, n);
        }
        printf("No
    ");
    }
    
    int main()
    {
        //freopen("test2.in", "r", stdin);
        //freopen("test2.out", "w", stdout);
        while (scanf("%d", &n) != EOF)
        {
            input();
            work();
        }
        return 0;
    }
    View Code

    代码(2n的做法)

    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cmath>
    #include <cstring>
    #include <algorithm>
    #include <set>
    #include <map>
    #include <queue>
    #include <vector>
    #include <string>
    #define LL long long
    
    using namespace std;
    
    const int maxN = 100005;
    int n, a[maxN], b[maxN];
    
    void input()
    {
        for (int i = 0; i < n; ++i) scanf("%d", &a[i]);
        for (int i = 0; i < n; ++i) scanf("%d", &b[i]);
    }
    
    void work()
    {
        int maxFrom, maxSum = 0;
        int from = 0, sum = 0, len = 2*n-1;
        for (int i = 0; i < len; ++i)
        {
            if (sum >= 0)
            {
                sum += a[i%n]-b[i%n];
                if (sum > maxSum)
                {
                    maxSum = sum;
                    maxFrom = from;
                }
            }
            else
            {
                sum = a[i%n]-b[i%n];
                from = i%n;
            }
        }
        sum = 0;
        for (int i = 0; i < n; ++i)
        {
            if (sum < 0)
            {
                printf("No
    ");
                return;
            }
            sum += a[(maxFrom+i)%n]-b[(maxFrom+i)%n];
        }
        if (sum >= 0) printf("Yes
    ");
        else printf("No
    ");
    }
    
    int main()
    {
        //freopen("test.in", "r", stdin);
        //freopen("test2.out", "w", stdout);
        while (scanf("%d", &n) != EOF)
        {
            input();
            work();
        }
        return 0;
    }
    View Code
  • 相关阅读:
    一些 SQLite技巧
    linux增加swap空间
    linux解压命令
    数据库常用语句
    服务器命令
    Clickhouse高可用配置总结
    MySQL笔记
    Linux查看硬件信息
    Greenplum安装
    ClickHouse学习笔记
  • 原文地址:https://www.cnblogs.com/andyqsmart/p/5460020.html
Copyright © 2011-2022 走看看