Algorithm Questions - C++ (5) Motion Rules

Classic knapsack problem

  • 0-1 backpack
  • full backpack
  • Multiple Backpacks
  • group backpack
0-1 backpack

There are N items, M-capacity knapsack, each item is selected once, the occupied space is v, and the value is w. What is the maximum value when the knapsack is filled as much as possible?

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int f[N][N],w[N],v[N];
int n,m;
int temp;
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)// i 是遍历的物品
        for(int j=0;j<=m;j++)// j 是当前容积
        {
    
    
            f[i][j] = f[i-1][j]; 
            // 本质上是当j>=v[i]时,f[i][j]=max(f[i-1][j],f[i-1][j-v[i]]+w[i])
            if(j>=v[i])
            {
    
    
            	f[i][j] = max(f[i][j],f[i-1][j-v[i]]+w[i]);
            }
            temp = max(temp,f[i][j]);
        }
    cout<<temp<<endl;
    return 0;
}
///
//一维解法
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 1005;
int f[N],v[N],w[N],temp;
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)//枚举物品
        for(int j=m;j>=v[i];j--)//枚举物品的容积 使用j做下标 因为容积有限 要倒着...?
        {
    
    	// j=v[i]~m ->f[i][j-v[i]]是不对的!
            // j=m~v[i] ->f[i-1][j-v[i]]
            f[j] = max(f[j],f[j-v[i]]+w[i]);
            temp = max(temp, f[j]);
        }
    cout<<temp<<endl;
    return 0;
}
full backpack

Unlimited selection of each item

#include<bits/stdc++.h>
using namespace std;
const int N = 1005;
int f[N][N],w[N],v[N];
int n,m;
int temp;
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        	for(int k=0;k*v[i]<=j;k++)//选几次
                f[i][j] = max(f[i][j],f[i-1][j-k*v[i]]+w[i]);
   	cout<<f[n][m]<<endl;
    return 0;
}
///
//优化两个循环//通过递推得到公式
// f[i][j] = max( f[i-1][j],f[i-1][j-v[i]]+w[i],f[i-1][j-v[i]*2]+2*w[i],...,)
// f[i][j-v[i]] = max(      f[i-1][j-v[i]]     ,f[i-1][j-v[i]*2]+w[i],...,)
//=> f[i][j] = max( f[i-1][j], f[i][j-v[i]]+w[i] )
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
        {
    
    
            f[i][j] = f[i-1][j];
            if(j>=v[i])
                f[i][j] = max(f[i][j],f[i][j-v[i]]+w[i]);
        }

   	cout<<f[n][m]<<endl;
    return 0;
}
///
//优化成为一维
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 1005;
int f[N],v[N],w[N],temp;
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i];
    for(int i=1;i<=n;i++)//枚举物品
        for(int j=v[i];j<=m;j++)//枚举物品的容积 
        {
    
    
            f[j] = max(f[j],f[j-v[i]]+w[i]);
            temp = max(temp, f[j]);
        }
    cout<<temp<<endl;
    return 0;
}
Multiple Backpacks

Each item has a fixed number of optional

#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 105;
int f[N][N],v[N],w[N],s[N];
int temp;
int main()
{
    
    
    cin>>n>>m;
    for(int i=1;i<=n;i++) cin>>v[i]>>w[i]>>s[i];
    
    for(int i=1;i<=n;i++)
        for(int j=0;j<=m;j++)
            for(int k=0;k*v[i]<=j && k<=s[i];k++)
            {
    
    
                // 注意这里的出现的一个问题
                // 不选的时候怎么表示??? 
                // 进入k循环之后k=0代表不选 所以k=0~s[i]它把选和不选都包含了...
                // 所以不用特地写出来了
                if(j>=v[i]*k)
                {
    
    
                    f[i][j] = max(f[i][j],f[i-1][j-v[i]*k]+w[i]*k);
                }
            }
    cout<<f[n][m]<<endl;
    return 0;
}

///
/// 优化
// f[i][j] = max( f[i-1][j],f[i-1][j-v[i]]+w[i],f[i-1][j-v[i]*2]+2*w[i],...,f[i-1][j-v[i]*s[i]]+s[i]*w[i])
// f[i][j-v[i]] = max(      f[i-1][j-v[i]]     ,f[i-1][j-v[i]*2]+w[i],...,f[i-1][j-v[i]*s[i]]+(s[i]-1)*w[i],f[i-1][j-v[i]*(s[i]+1)]+s[i]*w[i]) )
//=> f[i][j] = max( f[i-1][j], f[i][j-v[i]]+w[i] )
多出来一项 不能被削掉 需要用二进制 1 2 4 8 ,..,512 用二进制数表示原来的数
/// 把多个物品进行分组打包... 每组只能选一个...
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N = 20005;
int f[N],v[N],w[N];
int temp;
// 1 2 4 8 ..., 2^k , C
// C < 2^(k+1)
// 可以表示: 0~S
// 分好组之后对这些组做一次0-1背包
int main()
{
    
    
    cin>>n>>m;
    int cnt = 0;
    for(int i=1;i<=n;i++)
    {
    
    
        int a,b,c;
        cin>>a>>b>>c;
        //从1开始 1,2,4,8,...
        int k = 1;
        while(k<=c)
        {
    
    
            cnt++;
            v[cnt] = a*k;
            w[cnt] = b*k;
            // 个数-K
            c = c - k;
            k = k*2;
        }
        //剩下就是C
        if(c>0)
        {
    
    
            cnt++;
            v[cnt] = a*c;
            w[cnt] = b*c;
        }
    }
    // cnt 分成几组?
    n = cnt;
    for(int i=1;i<=n;i++)
        for(int j=m;j>=v[i];j--)
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}
group backpack

Only one item can be selected in the group

// 枚举第i组物品选几个?->完全背包
// 枚举第i组物品选哪个?->分组背包 第k个 几重循环???
// f[i,j] = max(f[i,j],f[i,j-v[i,k]]+w[i,k])
#include<bits/stdc++.h>
using namespace std;
int n,m;
const int N =105;
int f[N],s[N],w[N][N],v[N][N];
int main()
{
    
    
    cin>>n>>m;
    // 一维的先...
    for(int i=1;i<=n;i++)
    {
    
    
        cin>>s[i];//组里面的物品数
        for(int j=0;j<s[i];j++)
            cin>>v[i][j]>>w[i][j];
    }
    for(int i=1;i<=n;i++)
        for(int j=m;j>=0;j--)
            for(int k = 0;k<s[i];k++)
                if(j>=v[i][k])
                    f[j] = max(f[j],f[j-v[i][k]]+w[i][k]);
    cout<<f[m]<<endl;
    return 0;
}

Beginner DP Questions

Is the stair climbing problem actually a variant of the Fibonacci sequence?

746. Climb Stairs with Minimum Cost

class Solution {
    
    
public:
    int minCostClimbingStairs(vector<int>& cost) {
    
    
        vector<int> dp(cost.size());
        dp[0] = cost[0];
        dp[1] = cost[1];
        for(int i=2;i<cost.size();i++)
        {
    
    
            dp[i] = min(dp[i-1],dp[i-2])+cost[i];
        }
        // 为什么不直接输出dp[n-1]? 因为是前两步的最小值 这样直接输出会漏掉一种情况
        return min(dp[dp.size()-1],dp[dp.size()-2]); 
        // return dp[dp.size()-1];
    }
};

62. Different paths

Yang Hui Triangle is you again!
In fact, it is the permutation triangle of combination numbers.

class Solution {
    
    
public:
    int uniquePaths(int m, int n) {
    
    
        // dp[i][j] => (0,0)到(i,j)的路径种数
        // 转移状态: dp[i][j-1] + dp[i-1][j] => dp[i][j] // 不同路径
        // end = (m-1,n-1)
        // (0,0)->(i,0)路径种数 all 1 (0,0)->(0,i) all 1
        // (0,0)->(1,0) = 1
        // (0,0)->(2,0) = 1
        // 初始化dp vector<vector<T>> a(N,v) 类似于reshape v是一个vector<T>数组
        // vector<T> a(n,0) 初始化为全0的大小为n的T类型数组
        vector<vector<int>> dp(m, vector<int>(n,0));
        for(int i=0;i<m;i++) dp[i][0] = 1;
        for(int i=0;i<n;i++) dp[0][i] = 1;
        for(int i=1;i<=m;i++)
            for(int j=1;j<=n;j++)
                dp[i][j] = dp[i-1][j] + dp[i][j-1];
        return dp[m-1][n-1];
    }
};

一维数组 每次走到一个格子都是1种
///保存每次走到某列的结果 dp[i] 走到第i列时的总数
class Solution {
    
    
public:
    int uniquePaths(int m, int n) {
    
    
        vector<int> dp(n,1);
        for(int j=1;j<m;j++)
            for(int i=1;i<n;i++)
                dp[i] += dp[i-1];
        return dp[n-1];
    }
};

63. Different Paths II

There are obstacles in the deformed version of the path, so the obstacles must be removed before the calculation can be performed

class Solution {
    
    
public:
    int uniquePathsWithObstacles(vector<vector<int>>& obstacleGrid) {
    
    
        //获取行列
        int row = obstacleGrid.size();
        int column = obstacleGrid[0].size();

        vector<vector<int>> dp(row, vector<int> (column, 0));
        // 标记第一列和第一行的初始值
        for(int i=0;i<row && obstacleGrid[i][0] == 0;i++) dp[i][0] = 1;
        for(int i=0;i<column && obstacleGrid[0][i] == 0;i++) dp[0][i] = 1;

        for(int i=1;i<row;i++)
            for(int j=1;j<column;j++)
            //这里要限制当前的格子不是障碍物才行走... 漏掉这个会错
                if(obstacleGrid[i][j]==0)
                    dp[i][j] = dp[i-1][j] + dp[i][j-1];
        return dp[row-1][column-1]; 
    }
};

There is also a different path three, but DFS is used, so I will not brush this question here.

X mock questions

Rent a car and ride on Green Island.
One car can accommodate up to two people. The maximum load is M. The number of people is N. Input the weight information of N people.
What is the smallest car? 0<=N<=1e6

/
//第一种 用for来做...
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
    
    
    int m, n;
    cin >> m >> n;
    vector<int> w(n);
    for(int i=0; i<n; i++)
    {
    
    
        cin >> w[i];
    }
    sort(w.begin(), w.end());
    int count = 0;
    int i,j;
    for(i=0,j=n-1; i<j; )
    {
    
    
        int cur = w[i] + w[j];
        if(cur > m)
        {
    
    
            j--;
            count++;
        }
        else
        {
    
    
            i++;
            j--;
            count++;
        }
    }
    if(i == j)//只剩下一个人
    {
    
    
        count++;
    }
    cout << count << endl;
    return 0;
}
///
//第二种 用while
#include<iostream>
#include<algorithm>
#include<vector>
//平时可以用万能头,考试的时候应该不能用 
using namespace std;
int m,n;
// 逻辑模拟...0-0真的菜啊 
//一车最多坐两人 最大载重M 人数是N 
//问最小几辆车
 
int main()
{
    
    
	vector<int> w; 
	cin>>m>>n;
	for(int i=0;i<n;i++)
	{
    
    
		int x;
		cin>>x;
		w.push_back(x);
	}
	// 对权重排序
	// 其实已经想到这里了
	sort(w.begin(),w.end());
	//设置两个遍历对象
	int l = 0, r = w.size()-1;
	int res = 0;
	int cur = w[l]+w[r]; 
	// 测试的时候没有弹出 已经使用的权重...
	while(l<r)
	{
    
    
		if(cur>m)
		{
    
    
			//当前重量>m 移动右边的,因为左边的越小越可能满足条件
			r-=1;
			res+=1;//说明这个w[r]自己坐一辆车
			cur = w[l]+w[r];//更新当前的重量	
		}	
		else
		{
    
    
			//当前重量<=m 移动左边和右边
			r-=1;
			l+=1;
			res+=1;//两个人坐一辆车
			cur = w[l]+w[r]; 
		}
	} 
	if(l==r) res+=1;//?为啥...两者相遇=>总会剩下一个人
	cout<<res<<endl; 
	return 0;
} 

1042. Non-contiguous planting

There are at most three entry and exit paths in each garden, indicating that the out-degree and in-degree are 3

Only 4 colors can be dyed, so that the colors are different between connected gardens

Output a feasible solution, (output the total number of all feasible solutions)

// 这里直接给出邻接的坐标... 不是传入g[i][j] = 1 表示双向有边
// path:[[1,2] [2,3] [3,1]]====> 这里需要 x->y y->x ===> 
// 实际上是这么存储的 (1->2,2->1) (2->3,3->2) (1->3,3->1)  
// g[0] = {2}//代表第一个花园的邻接花园集合
// g[1] = {3}
// g[2] = {1}
// 所以x的下标需要映射y的坐标 所以y的下标需要映射x的坐标 它们都是从下标0开始所以第一下标要-1
class Solution {
    
    
public:
    vector<int> gardenNoAdj(int N, vector<vector<int>>& paths) {
    
    
        vector<vector<int>> g(N);
        vector<int> ans(N,0);

        for(int i=0;i<paths.size();i++)
        {
    
    
            // 这里取的是下标起始是0
            g[paths[i][0]-1].push_back(paths[i][1]);
            g[paths[i][1]-1].push_back(paths[i][0]);
        }

        for(int i=0;i<N;i++)
        {
    
    
            //遍历x->y 表
            int c[4] = {
    
    0};//染色数组
            for(int j=0;j<g[i].size();j++)//遍历当前的花园的邻接花园
                if(ans[g[i][j]-1]!=0)// ans[g[i][j]-1]的值: 第i个花园的邻接花园j的颜色编号
                    c[ ans[g[i][j]-1]-1 ] += 1;// c是被选择的颜色的次数,c[0] = N 第一种颜色被选N次
            for(int k = 0;k<4;k++)
            {
    
    // 这里k从0开始 颜色编号从1开始 所以颜色编号要+1
                if(c[k] == 0)//当颜色没被使用的话...
                {
    
    
                    ans[i] = k+1;
                    break;
                }
            }
        }
        return ans;
    }
};

If outputting the total of all scenarios:

class Solution {
    
    
public:
    vector<vector<int>> gardenNoAdj(int N, vector<vector<int>>& paths) {
    
    
        vector<vector<int>> g(N);
        vector<int> ans(N,0);
        vector<vector<int>> res;

        for(int i=0;i<paths.size();i++)
        {
    
    
            g[paths[i][0]-1].push_back(paths[i][1]);
            g[paths[i][1]-1].push_back(paths[i][0]);
        }

        dfs(g, ans, res, 0);

        return res;
    }

private:
    void dfs(vector<vector<int>>& g, vector<int>& ans, vector<vector<int>>& res, int index) {
    
    
        if (index == ans.size()) {
    
    
            res.push_back(ans);
            return;
        }
        vector<bool> used(5, false);
        for (int j = 0; j < g[index].size(); j++) {
    
    
            if (ans[g[index][j] - 1] != 0) {
    
    
                used[ans[g[index][j] - 1]] = true;
            }
        }
        for (int k = 1; k <= 4; k++) {
    
    
            if (!used[k]) {
    
    
                ans[index] = k;
                dfs(g, ans, res, index + 1);
                ans[index] = 0;
            }
        }
    }
};

Perfect position ----> LeetCode deformation

I thought it was very simple at first, thinking that the strings were arranged in a fixed order, but later I found that it was not right. After reading the solution on LeetCode, I finally understood how to write it!

#include<iostream>
#include<string>
#include<map>
using namespace std;
map <char,int> mp;
int main()
{
    
    
	string s;
	cin>>s;
	int left = 0, right = 0;
	for(int i=0;i<s.size();i++)
		mp[s[i]]+=1;
	int ans = 0x3f3f3f3f;
	int len = int(s.size()/4);
	//这里要特判一下 不然输出的就是很大的ans
	if(mp['W']==len && mp['A']==len && mp['D']==len && mp['S']==len)
    {
    
    
		cout<<0<<endl; 
		return 0; 
	}
	for(int right=0;right<s.size();right++)
	{
    
    
		mp[s[right]] -=1;// 有元素进入窗口 然后在窗外面的要减一 
		while(mp['W']<= len && mp['S']<=len && mp['A']<=len && mp['D']<=len)
		{
    
    
			ans = min(ans,right-left+1);
			mp[s[left++]]+=1;
            //求最小子串长 所以符合条件之后移动左端点  元素减少 窗外面的增加 
		}
	}
	cout<<ans<<endl;
	return 0;
}

Watch this first, you will understand a lot! Thank you for the gift of God!

synchronous double pointer

1234. Replace substring to get balanced string

There is a string of length n that contains only four characters 'Q', 'W', 'E', 'R'.

If all four characters appear exactly n/4 times in the string, then it is a "balanced string".

Given such a string , please make the original string into a "balanced sstring" by "replacing a substring" .s

You can use any other string of the same length as the "substring to be replaced" to complete the replacement.

Please return the minimum possible length of the substring to be replaced.

Returns 0 if the original string is itself a balanced string.


It should be noted here that because the length of the input string is a multiple of 4

So there is no need to judge the length first, but here is the number of occurrences of all characters in the string == n/4, the order is not required

So you can't judge in a fixed order ! QWER is reasonable, REWQ, EWQR are also reasonable...

How cunning!
insert image description here

The official said: Designate a small sliding window as the area to be replaced, and then judge the number of each character outside the window,

If the current number of characters is less than or equal to n/4, the condition is met to become a balanced string, otherwise it cannot be.

My head is full of question marks? Why?!

Oh, after looking carefully, I found that the number of times of satisfaction is n/4, so it must not become n/4 after it is greater than, because the characters outside can not be modified..., this is only the characters in the window can be modified, and the size of the window will be changed at this time Varies as the number of traversals increases

Use the left endpoint as left and the right endpoint as right to maintain a window. When right++, the window enters one more element, and when left++, the window decreases by one element, but the elements outside the window need to be calculated, so the array statistics Time to turn around...

When right++, the current number of elements outside the window –, when left++, the current number of elements outside the window ++

same direction double pointer

class Solution {
    
    
public:
    int balancedString(string s) {
    
    
        map <char,int> mp;
        int len = int(s.size()/4);
        for(int i=0;i<s.size();i++)
            mp[s[i]]+=1;
        if(mp['Q']==len && mp['E'] == len && mp['R'] == len && mp['W'] == len)
            return 0;
        int ans = 0x3f3f3f3f, left = 0;
        for(int right = 0;right<s.size();right++)
        {
    
    
            mp[s[right]]-=1;
            //只要满足条件就持续缩小窗口长度 即左指针右移动
            while(mp['Q']<=len && mp['E'] <= len && mp['R'] <= len && mp['W'] <= len)
            {
    
    
                ans = min(ans,right-left+1);
                mp[s[left++]]+=1;//窗口外的元素增加 所以次数也增加
            }
        }
        return ans;
    }
};

A similar problem...you can also use sliding windows

567. Permutation of Strings

Hash Table Elementary

  • First of all, if any permutation of a substring s1 is contained by another string s2, the lengths of matching substrings are equal
  • Enum permutation? No, you only need to have the same number of characters in the current substring as in the matching substring, because enumeration takes a lot of time, and you only need to know the number of permutations that can be combined

Grab these two tricks and use the above methods to try to solve them!

The difference is that the size of this window remains unchanged, while the size of the previous window will change. Here, focus on the judgment of the number of elements in the window size

Assume known: initial string s1: abcdabcand matching string s2: da
directly specify the size of the sliding window is s2.size()
to initialize two hash tables h, hashonly count [0-s2.size())the number of occurrences of all elements in it
[d,a] -> len = 2, hash={'a':1,'b':0,...,'d':1,...,'z':0}
[a,b,c,d,a,b,c] -> h = {'a':1,'b':1,'c':0,'d':0,...}
and when s1上sliding... count the number of elements...
moving the window to the right will cause the window The number of elements in the number ++, the number of elements removed –
[[a,b],c,d,a,b,c]->note that the operation is not performed here because it has been performed before...
[a,[b,c],d,a,b,c] -> h['a']--,h['c']++
[a,b,[c,d],a,b,c] -> h['b']--,h['d']++
[a,b,c,[d,a],b,c] -> h['c']--,h['a']++
h={'a':1,'b':0,'c':0,'d':1} ->hashmatch output true or output false
, but at this time we found that the last window has not been matched... so we still need to judge in the end h==hash
If both are the same, it means a match...
the mapping relationship is currenth[s[i-len]]--,h[s[i]]++

class Solution {
    
    
public:
    bool checkInclusion(string s1, string s2) {
    
    
        //特判s1>s2的情况 之前没有特判...
        if(s1.size()>s2.size()) return false;
        int win_size = s1.size();
        // 初始化hash1,hash2两个哈希表 并保存所有字母的映射关系
        vector<int> hash1(26,0);
        vector<int> hash2(26,0);
        for(int i=0;i<win_size;i++)
        {
    
    
            hash1[s1[i]-'a']+=1;
            hash2[s2[i]-'a']+=1;
        }
        for(int i=win_size;i<s2.size();i++)
        {
    
    
            //主要操作的是s2的匹配情况
            if(hash2 == hash1) return true;
            hash2[s2[i-win_size]-'a']-=1;
            hash2[s2[i]-'a']+=1;
        }
        // vector可以直接比较两个数组的大小情况 
        return hash1==hash2;
    }
};

number of text messages

Buy the number of text messages, give the budget M, and an array of size N, the subscript starts from 1, which represents how many text messages can be sent with price i, and find the maximum number of text messages that can be sent within the budget?

enter:

6 5

10 20 30 40 60

output:

70

Analysis: The budget is 6 yuan, you can send 10 text messages for 1 yuan, 20 for 2 yuan, 30 for 3 yuan, 40 for 4 yuan, and 60 for 5 yuan. A total of (5+1 yuan) is spent to send (60+10=70) text messages

EMMM, full backpack?

// 物品可以选无限次,但是总重量必须在容器范围内,求最大价值?
// 物品就是短信,价值是几条短信,容器大小是预算数目
// 速通!
#include<iostream>
using namespace std;
const int N =105;
int n,m;
int v[N],w[N],f[N];
int main()
{
    
    
    cin>>m>>n;
    for(int i=1;i<=n;i++)
    {
    
    
        cin>>w[i];
        v[i]=i;
	}
	for(int i=1;i<=n;i++)
        for(int j=v[i];j<=m;j++)
            f[j] = max(f[j],f[j-v[i]]+w[i]);
    cout<<f[m]<<endl;
    return 0;
}

Guess you like

Origin blog.csdn.net/daxuanzi515/article/details/130170168