[LeetCode] 1575. Count All Possible Routes
Solutions LeetCode DP DP优化
Lastmod: 2020-09-18 周五 19:12:19

1575. Count All Possible Routes

题目大意

给定一些不同的位置 $(N \le 100)$,给定起点和终点位置的标号,给定起始油量 $(F \le 200)$。

每次可以选择从当前位置走到任意其他位置花费每单位距离 $1$ 单位油。油量不能为负。

问,从起点到终点总共有多少种走法。

简要题解

法1:

本题中 $N$ 和 $F$ 的值都不是很大,考虑动态规划很容易设计出动态规划状态

$ dp[当前油量为 i ][当前所在点位为 j] $ 的方案数

注意到,油量总是从较小的推导较大的,由于位置彼此不同,不会存在相同油量的状态互相推导,于是这样的状态设计是可转移的。

很容易写出复杂度为 $O(N^2 F)$ 的动态规划。

代码实现

class Solution {
public:
    typedef pair<int,int> PII;
    int MO=1e9+7;
    void addv(int &x,int y)
    {
        if(x==-1) x=0;
        x+=y;
        if(x>=MO) x-=MO;
    }
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        int n=locations.size();
        vector<vector<int> > dp(n,vector<int>(fuel+1,-1));
        dp[start][fuel]=1;
        for(int j=fuel;j>0;j--)
        {
            for(int u=0;u<n;u++)
            {
                if(dp[u][j]==-1) continue;
                // cout<<u<<" "<<j<<endl;
                for(int v=0;v<n;v++)
                {
                    if(u==v) continue;
                    int d=abs(locations[u]-locations[v]);
                    if(d>j) continue;
                    addv(dp[v][j-d],dp[u][j]);
                }
            }
        }
        int ans=0;
        for(int i=0;i<=fuel;i++)
            if(dp[finish][i]!=-1) addv(ans,dp[finish][i]);
        return ans;
    }
};

法2:

注意到当三个位置的关系为 $a$,$b$,$c$ 这样排列时,如果从 $a$ 走到 $c$ 总会通过 $b$。

换句话说,所有从左侧点走到 $c$ 的都必须经过 $b$,因此考虑转移时只考虑从相邻位置转移过来。

$ dp[当前油量为 i ][当前所在点位为 j][状态为 k] $ 的方案数

$k = 0$,代表是停在 $j$ 点中转

$k = 1$,代表没有停,从左到右通过了 $j$

$k = 2$,代表没有停,从右到左通过了 $j$

复杂度

$T$: $O(NF)$

$S$: $O(NF)$

代码实现

const int M=105;
const int MF=205;
const int MO=1e9+7;

void addv(int &x,int y)
{
    x+=y;
    if(x>=MO) x-=MO;
}

int dp[MF][M][3];

class Solution {
public:
    int countRoutes(vector<int>& locations, int start, int finish, int fuel) {
        memset(dp,0,sizeof(dp));
        int n=locations.size();
        int vs=locations[start],ve=locations[finish];
        sort(locations.begin(),locations.end());
        for(int i=0;i<n;i++)
        {
            if(locations[i]==vs) start=i;
            if(locations[i]==ve) finish=i;
        }
        dp[fuel][start][0]=1;
        for(int i=fuel;i>0;i--)
        {
            for(int j=0;j<n;j++)
            {
                if(j)
                {
                    int d=locations[j]-locations[j-1];
                    if(d<=i)
                    {
                        addv(dp[i-d][j-1][0],dp[i][j][0]);
                        addv(dp[i-d][j-1][0],dp[i][j][1]);
                        
                        addv(dp[i-d][j-1][1],dp[i][j][0]);
                        addv(dp[i-d][j-1][1],dp[i][j][1]);
                    }
                }
                if(j!=n-1)
                {
                    int d=locations[j+1]-locations[j];
                    if(d<=i)
                    {
                        addv(dp[i-d][j+1][0],dp[i][j][0]);
                        addv(dp[i-d][j+1][0],dp[i][j][2]);
                        
                        addv(dp[i-d][j+1][2],dp[i][j][0]);
                        addv(dp[i-d][j+1][2],dp[i][j][2]);
                    }
                }
            }
        }
        int ans=0;
        for(int i=0;i<=fuel;i++)
            addv(ans,dp[i][finish][0]);
        return ans;
    }
};
Prev: [AtCoder] Beginner Contest 177 E - Coprime
Next: [AtCoder] Beginner Contest 176 E - Bomber