PAT第十一章专题复习

PAT第十一章专题复习

  • 本专题还需要多重复几遍,很不熟悉!!

  • 最长公共子序列

    • 变形:允许元素重复

    • A 1045 Favorite Color Stripe (30 分)

      • 思路:

        ①本题一开始想到的是用最长不下降子序列做。

        ②也可以用最长公共子序列来做,不过状态转移方程是经过变形的。

        //不允许元素重复的状态转移方程
        if(a[i] == b[j]){
            dp[i][j] = dp[i-1][j-1] + 1;
        }else{
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        }
        
        //允许元素重复的状态转移方程
        if(a[i] == b[j]){
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + 1;
        }else{
            dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        }
        //两者边界一样
        dp[i][0] = dp[0][j] = 0;
        
      • 代码实现

        #include<bits/stdc++.h>
        using namespace std; 
        const int maxc = 210;
        const int maxn = 10010;
        int n, m, L;
        int a[maxc], b[maxn];
        int dp[maxc][maxn];
        int main(){
        	cin >> n >> m;
        	for(int i = 1; i <= m; i++){
        		cin >> a[i];
        	}
        	cin >> L;
        	for(int i = 1; i <= L; i++){
        		cin >> b[i];
        	}
        	//边界
        	for(int i = 0; i <= m; i++)dp[i][0] = 0;
        	for(int i = 0; i <= L; i++)dp[0][i] = 0;
        	//状态转移方程
        	for(int i = 1; i <= m; i++){
        		for(int j = 1; j <= L; j++){
        			if(a[i] != b[j]){
        				dp[i][j] = max(dp[i-1][j], dp[i][j-1]);
        			}else{
        				dp[i][j] = max(dp[i-1][j], dp[i][j-1]) + 1;
        			}
        		}
        	} 
        	cout << dp[m][L];
        	return 0;
        } 
        
  • 背包问题

    • PAT A 1068 【01背包】(很难,需要再琢磨多遍】

      • 思路:

        ①选取几个数等于某一个值,可以看出来是01背包模型。

        ②难点在于如何运用这个模型。本题的c[i] = w[i]; 然后需要将w[i]按从大到小的顺序排序来满足输出的答案为最小字典序的条件。

        ③排好序之后,就套用01背包模板,同时需要有一个二维变量choice[i] [v]来记录当背包容量为v时是否选择第i件物品,选择值为1,反之为0.

        ④算完所有的dp数组值之后,判断dp[m]是否等于m,也就是题目中条件是否能够凑出等于m值的硬币选项。 这一点不同于01背包模型中,遍历所有dp数组寻找最大dp值来表示最大价值。是一个变形

        ⑤如果有dp[m] = m,接下来就寻找最优解。因为要输出字典序小的答案,所以需要倒序来遍历choice[i] [v]数组,当choice数组值为1的时候,就将flag数组记为1(flag数组用来表示最终答案选择哪几个)。最后遍历flag数组,将值为1的选项输出w[i]即硬币面额。

      • 难点:对于背包问题的状态转移过程理解还不够透彻,另外如何输出最小字典序也不清楚。

      • 代码实现

        #include<bits/stdc++.h>
        using namespace std; 
        int n, m; 
        int w[10010], dp[10010], flag[10010] = {0};
        int choice[10010][110];
        bool cmp(int a, int b){
        	return a > b;
        }
        int main(){
        	cin >> n>> m;
        	for(int i = 1; i <= n; i++){
        		cin >> w[i];
        	}
        	//逆序排序,这样配合后面的倒序枚举v,可以将字典序小的
        	//选项排在前面
        	sort(w + 1, w + n + 1, cmp);
        	//01背包模型
        	for(int i = 1; i <= n; i++){
        		for(int v = m; v >= w[i]; v--){
        			if(dp[v] <= dp[v-w[i]] + w[i]){
        				dp[v] = dp[v-w[i]] + w[i];
        				choice[i][v] = 1; //值为1表示选择第i件物品 
        			}
        			else choice[i][v] = 0;//值为0表示不选择第i件物品 
        		}
        	} 
        	if(dp[m] != m){//不相等表明不能恰好等于m 
        		printf("No Solution");
        	} else{
        		//记录最优路径
        		int k = n, v = m, num = 0, index = 0;
        		while(k >= 0){
        			if(choice[k][v] == 1){
        				flag[k] = 1; //选择第k个 
        				v -= w[k];
        				num++;
        			}
        			k--;
        		} 
        		for(int i = n; i >= 1; i--){
        			if(flag[i] == 1){
        				printf("%d", w[i]);
        				index++;
        				if(num != index)printf(" ");
        			}
        		} 
        	}
        	return 0;
        }
        
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

梦想总比行动多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值