笔试回顾1 | 翻大车

简单记一下自己没出的两道题吧

1.字符串最大得分

题目描述:

给一个字符串,里面都是小写字母,如果一个字母可以被标记(标记条件为隔壁字母和它相邻,相邻的意思就是b和a是相邻的,b和c是相邻的),那么就可以标记它和它隔壁的字母,获取它和隔壁的分数(a为1分,b为2分,以此类推),一个字母只能被标记一次。求最高分数。

输入:abb

输出:4

没做出来的原因:

贪心思想先入为主,想在当下就确定s[i]要跟前面的组还是跟后面的组,总结来说,脑子瓦特了没想到DP(其实想到了但自己把自己否了)。

代码块与解题思路:

#include<bits/stdc++.h>
using namespace std;
int main(){
// 最最最原式的线性DP,跟抛硬币例题没什么差别
// dp[i]表示前i个字符(包含i)中能获得的最大比分,每次选择是否加入s[i]
	string s;cin>>s;
	int n = s.size();
	vector<int> dp(n+10,0);//dp[i+1]表示s[i]前(算上s[i])能获得的最大分数
	for(int i=1;i<n;i++){//从第二个字符开始
        if(abs(s[i]-s[i-1])<=1){
        //要么不加s[i]这个字符,要么加,加的话一定是一次加两个的
			dp[i+1]=max(dp[i],dp[i-1]+s[i]-'a'+1+s[i-1]-'a'+1);
		}
        else	dp[i+1]=dp[i]
	}
	cout<<dp[n]<<endl;
	return 0;
}

2.小红闯沼泽地

题目描述:

在沼泽地中移动,1代表沼泽,0代表平地。

在相同的地方里移动消耗1,否则消耗2。

求从0,0处到n-1,m-1处最少消耗多少。

输入:3 3

1 0 0

1 1 1

0 0 1

输出:4

没做出来的原因:

1.自己并没有完全理解记忆化dfs。

先前的理解

该结点走过了所有能通往的地方后就为dp数组赋值,那么下次来的时候就不用继续搜了,直接获取dp数组的值就好了。

这种理解表面上好像可行,但它无法应对有可能往回走的场景,比如现在需求是从(1,1)走到(3,3)的最短路,可以左右下走;当前处于(2,1),是由(2,2)来的,那么在尝试走过(3,1)后就会为dp数组赋值了,但其实存在从(1,1)->(2,1)->(2,2)的走法,而dp数组赋值后就否定了这种走法。

理论点讲,每次到这个点的状态不同会影响着我能不能往左走,也就是说少记了一个状态。

这种思路只有在只能往右往下走的时候是有效的,而我对记忆化dfs片面的理解一直让我以为自己掌握了,这是翻车的主要原因。

正确的理解:

这道题用记忆化dfs和二维DP都不能应对向左走的情况,不过记忆化dfs能做到一种小剪枝,即dp数组记下起点到该点的距离,下次走到这的时候判断过来的路是否更短,只有更短才会继续往下走,这样就免去没必要走的一段路。

这道题的正确解法应该是Dijkstra最短路。

2.对自己的莫名自信 + 思维的不灵活

第二题没写就跑去写最后一题,第二题真的是最最最基础的DP了,跟抛257硬币凑27一样基础,自己没看出来属实是思维大寄了;还有就是自己一直坚信记忆化DFS的解法是没错的,怀疑没出正确结果是因为自己某个小地方写错了而已,其实就算自己没写Dijkstra,去掉向左的情况去跑也能拿90%的分。

代码块

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n,m;cin>>n>>m;
    vector<vector<int>> mad(n+10,vector<int>(m+10,0));
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>mad[i][j];//1代表沼泽,0代表平地
        }
    }
    vector<vector<int>> dire={{0,1},{1,0}};
    vector<vector<int>> visit(n+10,vector<int>(m+10,0));
    vector<vector<int>> dp(n+10,vector<int>(m+10,INT_MAX));//dp[i][j]表示从i,j位置到终点所花费的最小时间
    function<int(int,int)> dfs = [&](int i,int j){//坐标,记忆化搜索,返回值是从这个点到终点的距离
        if(i==n && j==n){
            return 0;
        }
        if(dp[i][j]!=INT_MAX)	return dp[i][j];
        int ans=INT_MAX/2;
        for(int k=0;k<2;k++){//往三个方向走
            int nexti=i+dire[k][0],nextj=j+dire[k][1];
            //dp[i][j]=INT_MAX表示这个点没走过或者无法走到终点,dp[i][j]!=INT_MAX表示已经走过了
            if(nexti>n || nextj>m || nexti<=0 || nextj<=0 || visit[nexti][nextj])    continue;//越界或走过了直接不走
            //沼泽进沼泽花费1时间,1*1
            //沼泽进入平地或平地进入沼泽花费2时间,1*0或0*1
            visit[nexti][nextj]=1;
            if(mad[nexti][nextj]*mad[i][j])    ans=min(ans,1+dfs(nexti,nextj));
            else    ans=min(ans,2+dfs(nexti,nextj));
            cout<<i<<" "<<j<<" "<<ans<<endl;
            visit[nexti][nextj]=0;
        }
        dp[i][j]=ans;
        return ans;
    };
    visit[1][1]=1;
    cout<<dfs(1,1)<<endl;
    return 0;
}

挺可惜的,解题决策不对,记忆化dfs的片面理解,吃一堑长一智吧。

全部评论

相关推荐

代码渣渣正在背八股:不招35岁以上,你的简历已进入人才库。
点赞 评论 收藏
分享
1 收藏 评论
分享
牛客网
牛客企业服务