【A】3742,3387,并查集

3742

如果是普通的方法的话,对于每个位置,都可能由其所位于的左侧或上侧到达,应当取他们的最大值,这是从分数角度;

设dp[i][j]为处于i,j位置时的分数

即dp[i][j]=max(dp[i-1][j],dp[i][j-1])+s[i][j]

但是如何限制花费?

就是即在限制花费的情况下,又要保证分数最大?

如果直接套用原来的动态规划,那只能得到其中一个最优的,无法保证全最优

一种思路是先得到所有花费可行的,然后在其中找分数最大的;

最直接的是,dfs+剪枝

向右走的次数i,向下走的次数j,

dfs(i,j,s,c)

令c+=c[i][j]

判断c是否大于k,如果是的话,就continue兼职,直接return

不然就说明这步是合法的,记s+=s[i][j]

判断当前位置是否为终点,如果是就记录s的结果,res=max(s,res)

尝试向右走,

判断i+1是否大于m-1,如果是的话,说明已经到最右侧了,就不能走了,continue

不然就是可以向右走,为dfs(i+1,j,s,c)

尝试向下走

复制代码
class Solution {
public:
    int res=-1,K,m,n;
    vector<vector<int>>G;
    vector<vector
    void dfs(int i,int j,int s,int c){
        //int flag=grid[i][j],ds,dc;
        int flag=G[i][j],ds,dc;
        if(flag==0){ds=0,dc=0;}
        else if(flag==1){ds=1,dc=1;}
        else{ds=2,dc=1;}
        //cout<<"i: "<<i<<" j: "<<j<<endl;
        if(c+dc>K){return;}
        //cout<<"i: "<<i<<" j: "<<j<<endl;
        if((i==m-1)&&(j==n-1)){res=max(res,s+ds);return;}
        if(i+1<=m-1){dfs(i+1,j,s+ds,c+dc);}
        if(j+1<=n-1){dfs(i,j+1,s+ds,c+dc);}
    }
    int maxPathScore(vector<vector<int>>& grid, int k) {
        K=k;
        G=grid;
        m=grid.size(),n=grid[0].size();
        //cout<<"m: "<<m<<" n: "<<n<<endl;
        dfs(0,0,0,0);
        return res;
    }
};

可行,但是超时了;

说要实现记忆化,但是缓存什么呢?就是应该保存什么信息呢?

可是到当前位置的花费cost可以是不固定的,会随着之前选择的路径的不同而不同,即可能有多个值,那又该选择哪个cost来进行保存呢?

所以要用一个三维数组,即在原来位置信息的基础上,再加一个cost或者score

如果是cost,就是去选这个状态下最高的score,那么dp的意义就是score

那么向下走为dp[i][j][c]=dp[i-1][j][c+c[i][j]]+s[i][j]

向右走为dp[i][j][c]=dp[i][j-1][c+c[i][j]]+s[i][j]

取这二者的最大值

最终,位置i和j是固定的,就是在c会有多个值,然后每个c的取值,都是在这个c下的最高score

但是,对于第三维c,如何去保存这个信息,如何去记录它?如果就是用数组的话,i和j的位置信息是连续的,就是数组上都是连续有值的,但是c不一定,可能是离散,就应该从0遍历到k,在其中找最大值

对于c的最大取值,就是向下走m次,向右走n次,都花费1,即m+n

如果使用vector来构建三维数组,初始化代码怎么写?就是锚定其一二三维的大小

对于确定的位置i和j可以使用for循环遍历,而对于c状态的不确定性,就没办法用for来遍历,还是得用dfs

由于在dfs当中要提前判断,所以定义dp[i][j][c]为以c的代价到了i和j的位置,值表示此时记录的最高score,还没计算(i,j)地块带来的影响

不行啊,这样的话dp赋值就说不清了,就应该是算上i,j地块影响后的值,这样dp才能赋值

如果此时dp

就是存在一个矛盾,如果要对下一步的dp进行赋值,就必须得让当前的DP有值,而一开始的时候,DP都是没值的,要填表

具体说就是,如果dp[i][j][c]表示处于i,j位置时的代价为c,还没有算上i,j位置的影响,那么要用上缓存,就应该判断dp[i][j][c]是否有值,如果有值,就说明这个参数的dfs之前来过;同样,如果没值,那么dp[i][j][c]就应该在此处被填值,但是这又和这个dfs的含义不同,因为dfs是要在这个参数下考虑该地块i,j的影响继续向下dfs赋值,所以赋值的应该是(i+1,j)或(i,j+1)等,如果就是要给(i,j)赋值,那么dp[i][j][c]就表示已经算上了(i,j)地块的影响,这又和一开始dp的定义矛盾

如果定义dp[i][j][c]为,以代价c到达i,j位置时所能达到的最高分数,那么从起点到终点,问题复杂度是越来越大的,即起点是最简子问题;

定义dfs(i,j,c)返回值为,从i,j出发,剩余K-C的预算时,所能获取的最大分数

ai说就应该让dp[i][j][c]和dfs(i,jc)的参数对齐,保持同一定义意义,那么就假设i,j,c中的c和s是算上i,j影响后的数值

这样dp[i][j][c]表示到达i,j时,付出代价为c后所达到的最高分数

如何定义dfs以辅助dp的赋值?或许并不需要

dfs(i,j,c,s)

先判断c是否超过k,如果超过就直接返回-1

不然说明此时的i,j,c是合法的

看此时的dp[i][j][c]是否填值了

是的话直接返回

不然,就

这里关键就是dp[i][j][c]填完值后,还可以继续被更新,被填值,不是说填完一次后就没事了

加了记忆化后

复制代码
class Solution {
public:
    int res=-1,K,m,n;
    vector<vector<vector<int>>>dp;
    vector<vector<int>>G;
    // vector<vector
    void dfs(int i,int j,int s,int c){
        //if(dp[i][j][c]){return;}
        //int flag=grid[i][j],ds,dc;
        if(c>K){return;}
        if(dp[i][j][c]>=s){return;}
        //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<" s: "<<s<<endl;
        dp[i][j][c]=s;

        // int flag=G[i][j],ds,dc;
        // if(flag==0){ds=0,dc=0;}
        // else if(flag==1){ds=1,dc=1;}
        // else{ds=2,dc=1;}

        //cout<<"i: "<<i<<" j: "<<j<<endl;
        // if(c+dc>K){return;}
        //cout<<"i: "<<i<<" j: "<<j<<endl;
        //if((i==m-1)&&(j==n-1)){res=max(res,s+ds);return;}
        int flag,dc,ds;
        if(i+1<=m-1){
            flag=G[i+1][j],dc=flag?1:0,ds=flag;
            dfs(i+1,j,s+ds,c+dc);
        }
        if(j+1<=n-1){
            flag=G[i][j+1],dc=flag?1:0,ds=flag;
            dfs(i,j+1,s+ds,c+dc);
        }
    }
    int maxPathScore(vector<vector<int>>& grid, int k) {
        m=grid.size(),n=grid[0].size();
        G=grid;
        K=k;
        dp.resize(m);
        for(int i=0;i<m;i++){
            dp[i].resize(n);
            for(int j=0;j<n;j++){
                dp[i][j].resize(k+1,-1);
            }
        }
        dfs(0,0,0,0);
        for(int i=0;i<=k;i++){
            //cout<<"c: "<<i<<" s: "<<dp[m-1][n-1][i]<<endl;
            res=max(dp[m-1][n-1][i],res);
        }
        return res;
    }
};

依然超时

这个就是说,对于前面的(i1,j1,c1)状态,先到的时候,先计算,以此为基点,往下算完一次遍历树;后面又有新的基点,到之前遍历过的节点时,如果s比不过还好,但如果超过了,就会造成覆盖,就会重新、再次遍历一次;这对于DFS遍历的记忆缓存化,是无法避免的

采用for循环的方式

保存c状态,就是在保存一种当前不是最大值,但由此c状态可能达到全局最大值的可能性

采用递推DP的方式,三层循环就是在遍历每种可能达到的状态

而当前这个状态是否可达,是由之前的for循环所确定的

即状态的for循环方向至关重要,必须满足从问题规模从小到大的方向,并在小问题规模时,递推下一步可达的所有未来

即对于每个状态i,j,c,都必须保证其上方和左方都已被遍历,唯一确定了此时保留的最优状态集合

这样,从左到右,从上到下的顺序是可以满足的

定义dp[i][j][c]为i,j,c状态下所能达到的最大分数,已考虑i,j地块的影响

在每个状态中,

先判断是否可达,即dp是否等于-1,如果是负一,说明其左侧和上侧都不可达该状态,直接过滤掉

有向下和向右两种状态转移方式

对于向下,给此状态能达到的它赋值,即dp[i+1][j][c+dc]=max(dp[i+1][j][c+dc],dp[i][j][c]+ds)

对于向右,dp[i][j+1][c+dc]=max(dp[i][j+1][c+dc],dp[i][j][c]+ds)

可以考虑初始化i=1;j=1的两种情况,然后for遍历时直接从1,1开始,避免处于边界时,对边界情况的检查

复制代码
class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        vector<vector<vector<int>>>dp(m,vector<vector<int>>(n,vector<int>(k+2,-1)));
        dp[0][0][0]=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                for(int c=0;c<=k;c++){
                    if(dp[i][j][c]==-1){continue;}
                    //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    int flag,dc,ds;
                    if(i+1<=m-1){
                        flag=grid[i+1][j],ds=flag,dc=flag?1:0;
                        //if(c+dc>k){continue;}
                        dp[i+1][j][c+dc]=max(dp[i+1][j][c+dc],dp[i][j][c]+ds);
                    }
                    if(j+1<=n-1){
                        flag=grid[i][j+1],ds=flag,dc=flag?1:0;
                        //if(c+dc>k){continue;}
                        dp[i][j+1][c+dc]=max(dp[i][j+1][c+dc],dp[i][j][c]+ds);
                    }
                }
            }
        }
        int res=-1;
        for(int c=0;c<=k;c++){
            //cout<<"c: "<<c<<" dp: "<<dp[m-1][n-1][c]<<endl;
            res=max(res,dp[m-1][n-1][c]);
        }
        return res;
    }
};

在状态转移当中,对于k,可能出现越界情况,对于开到k+1来说,但因为对于C状态的遍历是直到K,所以C最大取值是K+1,那么处理方式,一种就是把数组开到K+2,允许状态合法,但是不遍历;其二,就是直接进行判断,直接不允许对K+1赋值

复制代码
class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        vector<vector<vector<int>>>dp(m,vector<vector<int>>(n,vector<int>(k+1,-1)));
        dp[0][0][0]=0;
        for(int i=0;i<m;i++){
            for(int j=0;j<n;j++){
                for(int c=0;c<=k;c++){
                    if(dp[i][j][c]==-1){continue;}
                    //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    int flag,dc,ds;
                    if(i+1<=m-1){
                        flag=grid[i+1][j],ds=flag,dc=flag?1:0;
                        if(c+dc<=k){dp[i+1][j][c+dc]=max(dp[i+1][j][c+dc],dp[i][j][c]+ds);}
                    }
                    if(j+1<=n-1){
                        flag=grid[i][j+1],ds=flag,dc=flag?1:0;
                        if(c+dc<=k){dp[i][j+1][c+dc]=max(dp[i][j+1][c+dc],dp[i][j][c]+ds);}
                    }
                }
            }
        }
        int res=-1;
        for(int c=0;c<=k;c++){
            //cout<<"c: "<<c<<" dp: "<<dp[m-1][n-1][c]<<endl;
            res=max(res,dp[m-1][n-1][c]);
        }
        return res;
    }
};

对每个状态的确定,所需要的只是其所对应的上方和左方数据,如果已经确定了,那么就不再需要它们了

所以考虑滚动数组,即只保存当前要确定的位置i,j的上一行和左侧数据

先一直向右,确定出第一行,或者可以叫这步为初始化;

对于第二行及以后的处理

cur表示当前正在填表的一行,prev表示其上一行

那么在从左到右的过程中,可以保证每个位置的左侧和右侧都是被填的

其上方就是prev[j][c]

其左方就是cur[j-1][c]

复制代码
class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        //vector<vector<vector<int>>>dp(m,vector<vector<int>>(n,vector<int>(k+1,-1)));
        //vector<vector<int>>prev(n,vector<int>(k+1,-1)),cur(n,vector<int>(k+1,-1));
        //vector<vector<int>>prev(n),cur(n);
        vector<unordered_map<int,int>>prev(n),cur(n);
        //dp[0][0][0]=0;
        //prev[0].push_back(0);
        prev[0][0]=0;
        int flag,dc,ds,i=0;
        // for(int j=0;j<n;j++){
        //     for(int c=0;c<=k;c++){
        //         if(prev[j][c]==-1){continue;}
        //         if(j+1<=n-1){
        //             flag=grid[0][j],dc=flag?1:0,ds=flag;
        //             if(c+dc<=k){
        //                 prev[j+1][c+dc]=prev[j][c]+ds;
        //             }
        //         }
        //     }
        // }
        // for(int j=0;j<n;j++){
        //     for(int ci=0;ci<prev[j].size();ci++){
        //         if(j+1<=n-1){
        //             flag=grid[0][j],dc=flag?1:0,ds=flag;
        //             if(prev[j][ci]+dc<=k){
        //                 //prev[j+1][c+dc]=prev[j][c]+ds;
        //                 prev[j+1].push_back(prev[j][ci]+dc);

        //             }
        //         }
        //     }
        // }
        for(int j=0;j<n;j++){
            for(auto iter:prev[j]){
                if(j+1<=n-1){
                    flag=grid[i][j],dc=flag?1:0,ds=flag;
                    if(iter.first()+dc<=k){
                        prev[j+1][iter.first()+dc]=ds+iter.second();
                    }
                }
                if(i+1<=m-1){
                    flag=grid[i][j],dc=flag?1:0,ds=flag;
                    if(iter.first()+dc<=k){
                        cur[j][iter.first()+dc]=ds+iter.second();
                    }
                }
            }
        }
        i++;
        for(;i<m;i++){
            // for(auto iter:prev[0]){
            //     flag=grid[i][0],dc=flag?1:0,ds=flag;
            //     if(iter.first()+dc<=k){cur[0][iter.first()+dc]=ds+iter.second();
            // }
            vector<unordered_map<int,int>>tmp;
            for(int j=0;j<=n-1;j++){
                for(auto iter:cur[j]){
                    if(j+1<=n-1){
                        flag=grid[i][j],dc=flag?1:0,ds=flag;
                        if(iter.first()+dc<=k){
                            prev[j+1][iter.first()+dc]=ds+iter.second();
                        }
                    }
                    if(i+1<=m-1){
                        flag=grid[i][j],dc=flag?1:0,ds=flag;
                        if(iter.first()+dc<=k){
                            prev[j][iter.first()+dc]=ds+iter.second();
                        }
                    }
                }
            }
            tmp=cur;
            cur=prev;
            prev=cur;
        }
        for(int i=1;i<m;i++){
            for(int j=1;j<n;j++){
                for(int c=0;c<=k;c++){
                    if(dp[i][j][c]==-1){continue;}
                    //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    
                    if(i+1<=m-1){
                        flag=grid[i+1][j],ds=flag,dc=flag?1:0;
                        if(c+dc<=k){dp[i+1][j][c+dc]=max(dp[i+1][j][c+dc],dp[i][j][c]+ds);}
                    }
                    if(j+1<=n-1){
                        flag=grid[i][j+1],ds=flag,dc=flag?1:0;
                        if(c+dc<=k){dp[i][j+1][c+dc]=max(dp[i][j+1][c+dc],dp[i][j][c]+ds);}
                    }
                }
            }
        }
        int res=-1;
        for(int c=0;c<=k;c++){
            //cout<<"c: "<<c<<" dp: "<<dp[m-1][n-1][c]<<endl;
            res=max(res,dp[m-1][n-1][c]);
        }
        return res;
    }
};

class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        //vector<vector<vector<int>>>dp(m,vector<vector<int>>(n,vector<int>(k+1,-1)));
        //vector<vector<int>>prev(n,vector<int>(k+1,-1)),cur(n,vector<int>(k+1,-1));
        //vector<vector<int>>prev(n),cur(n);
        //vector<unordered_map<int,int>>prev(n),cur(n);
        vector<unordered_map<int,int>>cur(n);
        //dp[0][0][0]=0;
        //prev[0].push_back(0);
        //prev[0][0]=0;
        cur[0][0]=0;
        int flag,dc,ds,i=0;
        //1.第一行初始化,是否真的需要摘出来?
        // for(int j=0;j<n;j++){
        //     for(int c=0;c<=k;c++){
        //         if(prev[j][c]==-1){continue;}
        //         if(j+1<=n-1){
        //             flag=grid[i][j+1],dc=flag?1:0,ds=flag;
        //             if(c+dc<=k){
        //                 prev[j+1][c+dc]=prev[j][c]+ds;
        //             }
        //         }
        //         if(i+1<=m-1){
        //             flag=grid[i+1][j],dc=flag?1:0,ds=flag;
        //             if(c+dc<=k){
        //                 cur[j][c+dc]=ds+prev[j][c];
        //             }
        //         }
        //     }
        // }
        
        // for(int j=0;j<n;j++){
        //     for(int ci=0;ci<prev[j].size();ci++){
        //         if(j+1<=n-1){
        //             flag=grid[0][j],dc=flag?1:0,ds=flag;
        //             if(prev[j][ci]+dc<=k){
        //                 //prev[j+1][c+dc]=prev[j][c]+ds;
        //                 prev[j+1].push_back(prev[j][ci]+dc);

        //             }
        //         }
        //     }
        // }
        // for(int j=0;j<n;j++){
        //     for(auto iter:prev[j]){
        //         if(j+1<=n-1){
        //             flag=grid[i][j],dc=flag?1:0,ds=flag;
        //             if(iter.first()+dc<=k){
        //                 prev[j+1][iter.first()+dc]=ds+iter.second();
        //             }
        //         }
        //         if(i+1<=m-1){
        //             flag=grid[i][j],dc=flag?1:0,ds=flag;
        //             if(iter.first()+dc<=k){
        //                 cur[j][iter.first()+dc]=ds+iter.second();
        //             }
        //         }
        //     }
        // }
        //i++;
        //2.后续迭代,如何处理prev与cur的转换
        for(;i<m;i++){
            vector<unordered_map<int,int>>next(n);
            //vector<vector<int>>next(n,vector<int>(k+1,-1));
            for(int j=0;j<=n-1;j++){
                // for(auto iter:cur[j]){
                //     if(j+1<=n-1){
                //         flag=grid[i][j],dc=flag?1:0,ds=flag;
                //         if(iter.first()+dc<=k){
                //             prev[j+1][iter.first()+dc]=ds+iter.second();
                //         }
                //     }
                //     if(i+1<=m-1){
                //         flag=grid[i][j],dc=flag?1:0,ds=flag;
                //         if(iter.first()+dc<=k){
                //             prev[j][iter.first()+dc]=ds+iter.second();
                //         }
                //     }
                // }
                // for(int c=0;c<=k;c++){
                //     if(cur[j][c]==-1){continue;}
                //     //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                //     if(j+1<=n-1){
                //         flag=grid[i][j+1],dc=flag?1:0,ds=flag;
                //         if(c+dc<=k){
                //             cur[j+1][c+dc]=max(ds+cur[j][c],cur[j+1][c+dc]);
                //         }
                //     }
                //     if(i+1<=m-1){
                //         flag=grid[i+1][j],dc=flag?1:0,ds=flag;
                //         if(c+dc<=k){
                //             next[j][c+dc]=max(next[j][c+dc],ds+cur[j][c]);
                //         }
                //     }
                // }
                for(auto iter:cur[j]){
                    if(j+1<=n-1){
                        flag=grid[i][j+1],dc=flag?1:0,ds=flag;
                        if(iter.first+dc<=k){
                            cur[j+1][iter.first+dc]=max(cur[j+1][iter.first+dc],iter.second+ds);
                        }
                    }
                    if(i+1<=m-1){
                        flag=grid[i+1][j],dc=flag?1:0,ds=flag;
                        if(iter.first+dc<=k){
                            next[j][iter.first+dc]=max(next[j][iter.first+dc],iter.second+ds);
                        }
                    }
                }
            }
            // tmp=cur;
            // cur=prev;
            // prev=cur;
            //prev=cur;
            if(i!=m-1)cur=next;
        }
        // for(int i=1;i<m;i++){
        //     for(int j=1;j<n;j++){
        //         for(int c=0;c<=k;c++){
        //             if(dp[i][j][c]==-1){continue;}
        //             //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    
        //             if(i+1<=m-1){
        //                 flag=grid[i+1][j],ds=flag,dc=flag?1:0;
        //                 if(c+dc<=k){dp[i+1][j][c+dc]=max(dp[i+1][j][c+dc],dp[i][j][c]+ds);}
        //             }
        //             if(j+1<=n-1){
        //                 flag=grid[i][j+1],ds=flag,dc=flag?1:0;
        //                 if(c+dc<=k){dp[i][j+1][c+dc]=max(dp[i][j+1][c+dc],dp[i][j][c]+ds);}
        //             }
        //         }
        //     }
        // }
        int res=-1;
        // for(int c=0;c<=k;c++){
        //     //cout<<"c: "<<c<<" dp: "<<dp[m-1][n-1][c]<<endl;
        //     //cout<<"c: "<<c<<" dp: "<<cur[n-1][c]<<endl;
        //     res=max(res,cur[n-1][c]);
        // }
        for(auto iter:cur[n-1]){
            //cout<<"c: "<<c<<" dp: "<<cur[n-1][c]<<endl;
            res=max(res,iter.second);
        }
        return res;
    }
};

实现了滚动数组DP,将三维数组优化到二维数组

在此基础上,进一步以哈希表优化了对C的访问;

但是哈希表取代vector后,性能非但没有更好,反而还超时了部分样例

即哈希表优化,适用于稀疏数组;但这个题目背景下,c的值限制在(0,min(m+n,k))中,很稠密,所以用哈希表反而会加重时间、空间复杂度

复制代码
class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        vector<vector<int>>cur(n,vector<int>(k+1,-1));
        cur[0][0]=0;
        int flag,dc,ds,i=0;
        for(;i<m;i++){
            vector<vector<int>>next(n,vector<int>(k+1,-1));
            for(int j=0;j<=n-1;j++){
                for(int c=0;c<=k;c++){
                    if(cur[j][c]==-1){continue;}
                    //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    if(j+1<=n-1){
                        flag=grid[i][j+1],dc=flag?1:0,ds=flag;
                        if(c+dc<=k){
                            cur[j+1][c+dc]=max(ds+cur[j][c],cur[j+1][c+dc]);
                        }
                    }
                    if(i+1<=m-1){
                        flag=grid[i+1][j],dc=flag?1:0,ds=flag;
                        if(c+dc<=k){
                            next[j][c+dc]=max(next[j][c+dc],ds+cur[j][c]);
                        }
                    }
                }
            }
            if(i!=m-1)cur=next;
        }
        int res=-1;
        for(int c=0;c<=k;c++){
            res=max(res,cur[n-1][c]);
        }
        return res;
    }
};

细节小优化

在滚动数组的基础上继续优化

这个就是说,每个状态既然都由其上方和左方确定

然后又一直用一维数组来表示,那么按从左到右的顺序遍历时,每个状态被遍历完时,其下面状态的一个来源就被确定了,至于另一个来源,则在下一行时被确定,在那时取max;然后回到此时遍历的状态,其右侧的状态,就在此时也被补足了,用max;那么此时的状态也没用了,就可以被直接覆盖掉了,所以可以直接在这里进行覆盖

而不是等这一行都遍历完后再统一覆盖,也就是说,是遍历一个状态就覆盖一个状态,将这个方法的优化做到了极致

复制代码
class Solution {
public:

    int maxPathScore(vector<vector<int>>& grid, int k) {
        int m=grid.size(),n=grid[0].size();
        vector<vector<int>>cur(n,vector<int>(k+1,-1));
        cur[0][0]=0;
        int flag,dc,ds,i=0;
        for(;i<m;i++){
            //vector<vector<int>>next(n,vector<int>(k+1,-1));
            for(int j=0;j<=n-1;j++){
                vector<int>next_cur_tmp(k+1,-1);
                for(int c=0;c<=min(k,i+j);c++){
                    if(cur[j][c]==-1){continue;}
                    //cout<<"i: "<<i<<" j: "<<j<<" c: "<<c<<endl;
                    if(j+1<=n-1){
                        flag=grid[i][j+1],dc=flag?1:0,ds=flag;
                        if(c+dc<=k){
                            cur[j+1][c+dc]=max(ds+cur[j][c],cur[j+1][c+dc]);
                        }
                    }
                    if(i+1<=m-1){
                        flag=grid[i+1][j],dc=flag?1:0,ds=flag;
                        if(c+dc<=k){
                            next_cur_tmp[c+dc]=ds+cur[j][c];
                        }
                    }
                }
                if(i!=m-1)cur[j]=next_cur_tmp;
            }
            //if(i!=m-1)cur=next;
        }
        int res=-1;
        for(int c=0;c<=k;c++){
            res=max(res,cur[n-1][c]);
        }
        return res;
    }
};
优化c的遍历上界

3387

最直接的应该是遍历;但是不限制转换次数

对于第一天,假设有N种可行的转换次数,第二天有M种

那么直接遍历就是O(N*M)的复杂度

问题一就是如何知道有多少种可行的转换次数

如果在pairs当中,就说明两个节点之间是可连通的,就是可达的;

由此构建并查集,如果在同一个并查集下就说明他们之间是可以相互转换的;如果不在就说明不能;

最后应该是选取初始货币所在的那个并查集,以及进行完路径压缩后,有多少个节点,就是初始货币能转换到多少个最终状态,第一天的,加上边权,就意味着转换时的倍率;

第二天同理,但是第一天有N种可能的最后状态,如果第一天都不可达的并查集,在第二天更不可达,所以用的并查集和第一天是一样的;不对,第二天更新了pairs,所以构建的并查集和第一天不一样,对于每种状态,在其所在的并查集上,假设有X个元素,就有N*X的可能,

设根节点为基准,子节点到父节点的权值表示,子节点转到父节点的倍率,即子/父=边权,那么同一并查集下任意两个节点之间的转换公式(由a转到c,ac均接到父节点上)为d[a]/d[c]

1.对第一天,先根据pairs和rates构建第一天的并查集和权值数组

2.应当知道和初始货币类型同在一个并查集下的所有元素,假设结果为N

如何知道?

3.然后进行N次循环,表示在第一天后转到这N种货币,这里是外循环,并以tmp表示转换后的数量,tmp由转换公式得到

4.对第二天,根据pairs和rates构建第二天的并查集和权值数组

5.同样,应该知道第一天结束后的Ni货币所在并查集下的所有元素,结果为X

6.执行X次内循环,结果为res,由tmp和转换公式得到,取max(res)

最终max(res)为结果

复制代码
class Solution {
public:
    // unorderd_map<string>node;
    //vector<string>node;
    set<string>node;
    unordered_map<string,string>p1;
    unordered_map<string,double>d1;
    unordered_map<string,string>p2;
    unordered_map<string,double>d2;
    string find(string a,unordered_map<string,string>&p,unordered_map<string,double>&d){
        if(p[a]==a){
            return a;
        }
        string origin=p[a];
        p[a]=find(p[a],p,d);
        d[a]*=d[origin];
        return p[a];
    }
    void unite(string a,string b,double v,unordered_map<string,string>&p,unordered_map<string,double>&d){
        string ra=find(a,p,d),rb=find(b,p,d);
        if(ra==rb){return;}
        p[ra]=rb;
        d[ra]=v*d[b]/d[a];
    }
    double maxAmount(string initialCurrency, vector<vector<string>>& pairs1, vector<double>& rates1, vector<vector<string>>& pairs2, vector<double>& rates2) {
        for(int i=0;i<pairs1.size();i++){
            string a=pairs1[i][0],b=pairs1[i][1];
            if(p1.find(a)==p1.end()){
                p1[a]=a;
                //node.push_back(a);
                node.insert(a);
                d1[a]=1.0;
            }
            if(p1.find(b)==p1.end()){
                p1[b]=b;
                node.insert(b);
                //node.push_back(b);
                d1[b]=1.0;
            }
            unite(a,b,rates1[i],p1,d1);
        }
        for(int i=0;i<pairs2.size();i++){
            string a=pairs2[i][0],b=pairs2[i][1];
            if(p2.find(a)==p2.end()){
                p2[a]=a;
                //node.push_back(a);
                node.insert(a);
                d2[a]=1.0;
            }
            if(p2.find(b)==p2.end()){
                p2[b]=b;
                //node.push_back(b);
                node.insert(b);
                d2[b]=1.0;
            }
            unite(a,b,rates2[i],p2,d2);
        }
        double res=1.0;
        //for(int i=0;i<node.size();i++){
        for(const string&cur:node){
            // if(node[i]==initialCurrency){continue;}
            // if(find(node[i],p1,d1)!=find(initialCurrency,p1,d1)){continue;}
            // if(find(node[i],p2,d2)!=find(initialCurrency,p2,d2)){continue;}
            // double tmp=d1[initialCurrency]/d1[node[i]];
            // res=max(res,d2[node[i]]/d2[initialCurrency]*tmp);
            if(cur==initialCurrency){continue;}
            if(find(cur,p1,d1)!=find(initialCurrency,p1,d1)){continue;}
            if(find(cur,p2,d2)!=find(initialCurrency,p2,d2)){continue;}
            double tmp=d1[initialCurrency]/d1[cur];
            res=max(res,d2[cur]/d2[initialCurrency]*tmp);
        }
        return res;
    }
};

class Solution {
public:
    // unorderd_map<string>node;
    //vector<string>node;
    unordered_map<string,int>m;
    //set<string>node;
    int cnt=0;
    // unordered_map<string,string>p1;
    // unordered_map<string,double>d1;
    // unordered_map<string,string>p2;
    // unordered_map<string,double>d2;
    vector<int>p1,p2;
    vector<double>d1,d2;
    // unordered_map<string,double>d1;
    // unordered_map<string,string>p2;
    // unordered_map<string,double>d2;
    // string find(string a,unordered_map<string,string>&p,unordered_map<string,double>&d){
    //     if(p[a]==a){
    //         return a;
    //     }
    //     string origin=p[a];
    //     p[a]=find(p[a],p,d);
    //     d[a]*=d[origin];
    //     return p[a];
    // }
    int find(int a,vector<int>&p,vector<double>&d){
        if(p[a]==a){
            return a;
        }
        int origin=p[a];
        p[a]=find(p[a],p,d);
        d[a]*=d[origin];
        return p[a];
    }
    // void unite(string a,string b,double v,unordered_map<string,string>&p,unordered_map<string,double>&d){
    //     string ra=find(a,p,d),rb=find(b,p,d);
    //     if(ra==rb){return;}
    //     p[ra]=rb;
    //     d[ra]=v*d[b]/d[a];
    // }
    void unite(int a,int b,double v,vector<int>&p,vector<double>&d){
        int ra=find(a,p,d),rb=find(b,p,d);
        if(ra==rb){return;}
        p[ra]=rb;
        d[ra]=v*d[b]/d[a];
    }
    double maxAmount(string initialCurrency, vector<vector<string>>& pairs1, vector<double>& rates1, vector<vector<string>>& pairs2, vector<double>& rates2) {
        //m[initialCurrency]=cnt++;
        //cout<<"test1"<<endl;
        for(int i=0;i<pairs1.size();i++){
            string a=pairs1[i][0],b=pairs1[i][1];
            if(m.find(a)==m.end()){
                cout<<a<<endl;
                p1.push_back(cnt);
                d1.push_back(1.0);
                p2.push_back(cnt);
                d2.push_back(1.0);
                // p1[cnt]=cnt;
                // d1[cnt]=1.0;
                m[a]=cnt++;
                //node.push_back(a);
                //node.insert(a);
            }
            if(m.find(b)==m.end()){
                cout<<b<<endl;
                //p1[cnt]=cnt;
                //node.insert(b);
                //node.push_back(b);
                //d1[cnt]=1.0;
                p1.push_back(cnt);
                d1.push_back(1.0);
                p2.push_back(cnt);
                d2.push_back(1.0);
                m[b]=cnt++;
            }
            cout<<"test3,a:"<<m[a]<<" b:"<<m[b]<<endl;
            unite(m[a],m[b],rates1[i],p1,d1);
        }
        //cout<<"test4,a"<<endl;
        for(int i=0;i<pairs2.size();i++){
            string a=pairs2[i][0],b=pairs2[i][1];
            if(m.find(a)==m.end()){
                p2.push_back(cnt);
                d2.push_back(1.0);
                m[a]=cnt++;
            }
            if(m.find(b)==m.end()){
                p2.push_back(cnt);
                d2.push_back(1.0);
                m[b]=cnt++;
            }
            unite(m[a],m[b],rates2[i],p2,d2);
        }
        cout<<"test2,并查集1: "<<p1.size()<<" 2:"<<p2.size()<<endl;
        double res=1.0;
        //for(int i=0;i<node.size();i++){
        // for(const string&cur:node){
        for(int i=0;i<p2.size();i++){
            // if(node[i]==initialCurrency){continue;}
            // if(find(node[i],p1,d1)!=find(initialCurrency,p1,d1)){continue;}
            // if(find(node[i],p2,d2)!=find(initialCurrency,p2,d2)){continue;}
            // double tmp=d1[initialCurrency]/d1[node[i]];
            // res=max(res,d2[node[i]]/d2[initialCurrency]*tmp);
            //if(cur==initialCurrency){continue;}
            if(i<p1.size()&&(find(i,p1,d1)!=find(m[initialCurrency],p1,d1))){continue;}
            //if((find(i,p1,d1)!=find(0,p1,d1))){continue;}
            if(find(i,p2,d2)!=find(m[initialCurrency],p2,d2)){continue;}
            //if(i==m[initialCurrency]){continue;}
            //cout<<i<<endl;
            double tmp=1.0;
            if(i<p1.size()){tmp=d1[m[initialCurrency]]/d1[i];}
            res=max(res,d2[i]/d2[m[initialCurrency]]*tmp);
        }
        return res;
    }
};

class Solution {
public:
    unordered_map<string,int>m;
    int cnt=0;
    vector<int>p1,p2;
    vector<double>d1,d2;
    int find(int a,vector<int>&p,vector<double>&d){
        if(p[a]==a){
            return a;
        }
        int origin=p[a];
        p[a]=find(p[a],p,d);
        d[a]*=d[origin];
        return p[a];
    }
    void unite(int a,int b,double v,vector<int>&p,vector<double>&d){
        int ra=find(a,p,d),rb=find(b,p,d);
        if(ra==rb){return;}
        p[ra]=rb;
        d[ra]=v*d[b]/d[a];
    }
    double maxAmount(string initialCurrency, vector<vector<string>>& pairs1, vector<double>& rates1, vector<vector<string>>& pairs2, vector<double>& rates2) {
        for(int i=0;i<pairs1.size();i++){
            string a=pairs1[i][0],b=pairs1[i][1];
            if(m.find(a)==m.end()){m[a]=cnt++;}
            if(m.find(b)==m.end()){m[b]=cnt++;}
        }
        for(int i=0;i<pairs2.size();i++){
            string a=pairs2[i][0],b=pairs2[i][1];
            if(m.find(a)==m.end()){m[a]=cnt++;}
            if(m.find(b)==m.end()){m[b]=cnt++;}
        }
        p1.resize(cnt),p2.resize(cnt),d1.resize(cnt),d2.resize(cnt);
        for(int i=0;i<cnt;i++){
            p1[i]=i,p2[i]=i;
            d1[i]=1.0,d2[i]=1.0;
        }
        for(int i=0;i<pairs1.size();i++){
            string a=pairs1[i][0],b=pairs1[i][1];
            unite(m[a],m[b],rates1[i],p1,d1);
        }
        for(int i=0;i<pairs2.size();i++){
            string a=pairs2[i][0],b=pairs2[i][1];
            unite(m[a],m[b],rates2[i],p2,d2);
        }
        double res=1.0;
        for(int i=0;i<cnt;i++){
            if((find(i,p1,d1)!=find(m[initialCurrency],p1,d1))){continue;}
            if(find(i,p2,d2)!=find(m[initialCurrency],p2,d2)){continue;}
            double tmp=d1[m[initialCurrency]]/d1[i];;
            res=max(res,d2[i]/d2[m[initialCurrency]]*tmp);
        }
        return res;
    }
};

并查集

假设以A为基点,就是root;

399

1.遍历对数组equations,让被除数作为父亲,即A/B=C时,P[A]=B;这样有i个元素的equtions,遍历完后有i个root

2.尝试合并这些节点

但是如何保证一遍遍历确实是不遗漏的?

合并的标准的是什么?就是给出A和B,C和D的关系后,那么他们之间就一定要合并吗?应该是至少有关联的元素才能合并,但是在一开始时都没合并时,没法判断是否真的有关系(因为这个关系可能在后面),以及如果真的有关系,又怎么判断出来?比如a->b,b->c,在接到第二个关系时,此时只要两个root,即b和c,那么只判断根是无法断定他们直接有关系的,即find没用,那怎么知道什么时候该合并,什么时候不合并?

如果知道要合并,那该怎么合并,随机选一个为新的根节点,将边权信息记录在没被选为根节点的那个旧根节点当中;后续遍历到的时候再进行路径压缩,边权更新关系为d[a]=d[a]*d[p[a]]

3.对于解题,c与d,首先find[c]和find[d]是不是相等,如果不相等就说明他们之间没关系,直接返回-1;不然c/d=d[c]/d[d]

这是以c和d都直接挂载到根节点为例的,应该需要判断一下c和d是不是真的挂载在根节点,即p[c]=find(c),如果不是,需要再while一下去更新数值;

不应当不在同一集合就直接合并,比如A->B,C->D,此时该如何合并?AB和CD都没关系,怎么合并?

对于合并的标准,就是等式两侧的变量(设为A/B=C),如何其属于的根节点不同,就该合并;此时根据二者是否为各自的根节点,又该进行讨论;

如果AB均是自己的根节点,那么P[A]=B,d[a]=c

如果其中一个是,一个不是,以A是为例,应当找到B的根节点,设为D,即D=find(B),然后再P[A]=D,d[a]=c*d[b](即b是a到新根节点d上的一个节点)

如果都不是,都找到,记E=find(A),那么P[E]=D,d[e]=c*d[a]*d[b]

如何记录数据结构?

每个节点要记录自己的名字string,和到父节点的权值int,以及自己的父节点

首先要知道有几个节点,假设有n个;

用int数组d记录权值关系;

如何记录连接关系?就用string数组p

但是如何知道一共有多少个节点?有没有什么好方法?

使用哈希表

对于a->b和b->c的情况,如果就

对于a->b和a->c的情况,如果使用数组去建模并查集,都会造成覆盖情况,该怎么处理?

现在代码存在问题,即默认将equtions当中的每个元素都作为初始的并查集去建立节点关系,这样容易造成覆盖问题

先判断分子分母是否已经有父亲了,如果有了,直接执行合并,不然就执行构建

相关推荐
gihigo19981 小时前
CHAN时延估计算法(二维/三维定位实现)
算法
freexyn2 小时前
Matlab自学笔记七十六:表达式的展开、因式分解、化简、合并同类项
笔记·算法·matlab
样例过了就是过了2 小时前
LeetCode热题 不同路径
c++·算法·leetcode·动态规划
dog2502 小时前
圆锥曲线和二次曲线
开发语言·网络·人工智能·算法·php
Wadli2 小时前
27.单调队列
算法
Navigator_Z2 小时前
LeetCode //C - 1031. Maximum Sum of Two Non-Overlapping Subarrays
c语言·算法·leetcode
Wect2 小时前
LeetCode 97. 交错字符串:动态规划详解
前端·算法·typescript
爱学习的张大3 小时前
具身智能论文问答(三):Open VLA
人工智能·算法
wearegogog1233 小时前
基于Q-learning的栅格地图路径规划MATLAB仿真程序
开发语言·算法·matlab