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\n");
            return;
        }
        else i = min(i+j, n);
    }
    printf("No\n");
}

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\n");
            return;
        }
        sum += a[(maxFrom+i)%n]-b[(maxFrom+i)%n];
    }
    if (sum >= 0) printf("Yes\n");
    else printf("No\n");
}

int main()
{
    //freopen("test.in", "r", stdin);
    //freopen("test2.out", "w", stdout);
    while (scanf("%d", &n) != EOF)
    {
        input();
        work();
    }
    return 0;
}
View Code

 

posted on 2016-05-04 22:13  AndyQsmart  阅读(1104)  评论(0编辑  收藏  举报

导航