zoukankan      html  css  js  c++  java
  • 洛谷试炼场 动态规划专练

    // 最近才发现洛谷也有整理好的分类题集,于是前来加强练习一番。
    普及练习场


    在普及练习场分分钟解决了动态规划的背包问题,最后卡在 -->

    P1064 金明的预算方案

    重学了一遍分组背包终于解决了该问题。 参考背包问题九讲
    注意分组后不能重复选啊!!!
     

    解题思路

    将附件做01背包求得在不同金钱下购买若干附件获得的最大价值,将所有方案加入到主件。现在新的主件中只能选择购买其中一种,于是转化为分组背包问题。
     

    AC代码

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    using namespace std;
    
    struct Node {
    	int v, p;   // 花费 与 价值
    };
    vector<Node> Main[64];
    vector<Node> Part[64];
    
    int dp[33000];
    int main() {
    	int V, m;
    	cin>>V>>m;
    	for(int i=1;i<=m;i++) {
    		int v, p, q;
    		scanf("%d %d %d", &v, &p, &q);
    		p *= v;
    		if(!q) Main[i].push_back((Node){v, p});
    		else
    			Part[q].push_back((Node){v, p});
    	}
    	for(int i=1;i<=m;i++) {
    		if(Part[i].size()==0) continue;
    
    		// i主件的j附件 01背包
    		memset(dp, 0, sizeof(dp));
    		for(int j=0;j<Part[i].size();j++) {
    			int v = Part[i][j].v, p = Part[i][j].p;
    			for(int k=V;k>=v;k--) {
    				dp[k] = max(dp[k], dp[k-v]+p);
    			}
    		}
    		// 金钱j+Main[i][0].v 最多获得 dp[j]+Main[i][0].p
    		// 全部放入Main分组
    		int last = 0;
    		for(int j=1;j+Main[i][0].v<=V;j++) {
    			if(dp[j]>last)
    				Main[i].push_back((Node){j+Main[i][0].v, dp[j]+Main[i][0].p}), last = dp[j];
    		}
    	}
    
    	// 分组背包
    	memset(dp, 0, sizeof(dp));
    	for(int i=1;i<=m;i++) {
    		for(int k=V;k>=0;k--) {
    			for(int j=0;j<Main[i].size();j++) {
    				int v = Main[i][j].v, p = Main[i][j].p;
    				if(k>=v)
    					dp[k] = max(dp[k], dp[k-v]+p);
    			}
    		}
    	}
    	printf("%d
    ", dp[V]);
        return 0;
    }
    

    线性动态规划

    P1020 导弹拦截

    思路:单次拦截导弹个数就是求(不上升的) LIS , 总的拦截次数怎么求呢 ? 尝试贪心莽了一次发现第二问几乎都WA了。一看题解就是求(上升的) LIS 。。。
    Why?

                 西江月 · 证明
    

    即得易见平凡,仿照上例显然。留作习题答案略,读者自证不难。
    反之亦然同理,推论自然成立,略去过程 Q E D,由上可知证毕。

    代码:

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    using namespace std;
    
    int h[100010];
    int dp[100010];
    int main() {
    	int n = 0, v;
    	while(scanf("%d", &v)!=EOF) {
    		h[++n] = -v;
    	}
    
    	dp[1] = h[1];
    	int len1 = 1;
    	for(int i=2;i<=n;i++) {
    	    if(dp[len1]<=h[i]) dp[++len1] = h[i];
    	    else {
    		    int pos = upper_bound(dp+1, dp+len1+1, h[i]) - dp;
    		    dp[pos] = h[i];
    	    }
    	}
    	printf("%d
    ", len1);
    
    	for(int i=1;i<=n;i++)
    		h[i] = -h[i];
    		
    	dp[1] = h[1];
    	len1 = 1;
    	for(int i=2;i<=n;i++) {
    	    if(dp[len1]<h[i]) dp[++len1] = h[i];
    	    else {
    		    int pos = lower_bound(dp+1, dp+len1+1, h[i]) - dp;
    		    dp[pos] = h[i];
    	    }
    	}
    	printf("%d
    ", len1);
    
    	return 0;
    }
    

    P1091 合唱队形

    思路:分别求出从前往后与从后往前到达第 i 位 的最长上升子序列长度 LIS1[i], LIS2[i],那么 ans = max(n+1 - LIS1[i] - LIS2[i]) 。
    此题 n 较小,O(n^2)可以通过。
    以下为 O(nlogn)复杂度 求 LIS 的算法。
    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    int h[110], n;
    int d1[110], d2[110];		// 辅助数组
    int LIS1[110], LIS2[110];	// LIS[i] : 以h[i]结尾的最长LIS长度 
    
    void getLIS(int d[], int LIS[]) {
    	d[1] = h[1]; LIS[1] = 1;
    	int len = 1;
    	// cout<<LIS[1];
    	for(int i=2;i<=n;i++) {
    		if(h[i]>d[len]) d[++len] = h[i], LIS[i] = len;
    		else {
    			int pos = lower_bound(d+1, d+len+1, h[i]) - d;
    			d[pos] = h[i];
    			LIS[i] = pos;
    		}
    		// cout<<' '<<LIS[i];
    	}
    	// puts("");
    }
    
    int main() {
    	cin>>n;
    	for(int i=1;i<=n;i++)
    		cin>>h[i];
    
    	getLIS(d1, LIS1);
    
    	reverse(h+1, h+1+n);
    	getLIS(d2, LIS2);
    	reverse(LIS2+1, LIS2+1+n);
    
    	int ans = n;
    	for(int i=1;i<=n;i++) {
    		ans = min(ans, n+1-(LIS1[i]+LIS2[i]));
    	}
    	printf("%d
    ", ans);
    	return 0;
    }
    

    P1880 [NOI1995]石子合并

    思路:区间DP 模板题变形,区间变成了环形区间,加一个取模操作即可。
    代码:

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    int h[110];
    int sum[110][110];
    int dp[110][110];
    
    int main() {
    	int n; cin>>n;
    	for(int i=0;i<n;i++)
    		scanf("%d", &h[i]);
    
    	for(int i=0;i<n;i++) {
    		sum[i][i] = h[i];
    		for(int j=i+1;j<i+n;j++) {
    			sum[i][j%n] = sum[i][(j-1+n)%n] + h[j%n];
    		}
    	}
    
    	// for(int i=0;i<n;i++) {
    	// 	for(int j=0;j<n;j++)
    	// 		printf("%d%c", sum[i][j], j==n-1?'
    ':' ');
    	// }
    
    	memset(dp, 0x3f, sizeof(dp));
    	for(int i=0;i<n;i++) dp[i][i] = 0;
    	int ans = 0x3f3f3f3f;
    	for(int len=1;len<n;len++) {
    		for(int i=0;i<n;i++) {
    			int j = i+len;
    			for(int k=i;k<j;k++) {
    				dp[i][j%n] = min(dp[i][j%n], dp[i][k%n]+dp[(k+1)%n][j%n] + sum[i][j%n]);
    				if(len==n-1)
    					ans = min(ans, dp[i][j%n]);
    			}
    		}
    	}
    	printf("%d
    ", ans);
    
    	memset(dp, 0, sizeof(dp));
    	for(int len=1;len<n;len++) {
    		for(int i=0;i<n;i++) {
    			int j = i+len;
    			for(int k=i;k<j;k++) {
    				dp[i][j%n] = max(dp[i][j%n], dp[i][k%n]+dp[(k+1)%n][j%n] + sum[i][j%n]);
    				ans = max(ans, dp[i][j%n]);
    			}
    		}
    	}
    	printf("%d
    ", ans);
    
    	return 0;
    }
    

    P1140 相似基因

    思路:设 dp[i][j] 表示 碱基S前 i 位与碱基T前 j 位匹配的最大相似度。
    dp[i][j] = max{dp[i-1][j-1] + S[i]与T[j]的相似度, dp[i][j-1] + 空缺与T[j]的相似度, dp[i-1][j] + S[i]与空缺的相似度}
    代码:

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    char S[110], T[110];
    int n, m;
    int dp[110][110]; // S[i] 与 T[j] 位匹配的最大值
    const int v[5][5] = {
    	{5, -1, -2, -1, -3},
    	{-1, 5, -3, -2, -4},
    	{-2, -3, 5, -2, -2},
    	{-1, -2, -2, 5, -1},
    	{-3, -4, -2, -1, 0}
    };
    
    int id(char c) {
    	if(c=='A') return 0;
    	else if(c=='C') return 1;
    	else if(c=='G') return 2;
    	else if(c=='T') return 3;
    	else return 4;
    }
    int main() {
    	cin>>n>>S+1;
    	cin>>m>>T+1;
    
    	for(int i=1;i<=n;i++) S[i] = id(S[i]);
    	for(int i=1;i<=m;i++) T[i] = id(T[i]);
    
    	for(int i=1;i<=n;i++)
    		for(int j=1;j<=m;j++)
    			dp[i][j] = -0x3f3f3f;
    	dp[0][0] = 0;
    	for(int i=1;i<=m;i++) dp[0][i] = dp[0][i-1] + v[4][T[i]];
    	for(int i=1;i<=n;i++) dp[i][0] = dp[i-1][0] + v[S[i]][4];
    
    	for(int i=1;i<=n;i++) {
    		for(int j=1;j<=m;j++) {
    			dp[i][j] = max(dp[i][j], dp[i-1][j-1] + v[S[i]][T[j]]);
    			dp[i][j] = max(dp[i][j], dp[i-1][j] + v[S[i]][4]);
    			dp[i][j] = max(dp[i][j], dp[i][j-1] + v[4][T[j]]);
    		}
    	}
    	printf("%d
    ", dp[n][m]);
    	return 0;
    }
    
  • 相关阅读:
    AE二次开发,解决子窗体使用父窗体的AxControl控件
    ArcEngine二次开发中运行出现There is no Spatial Analyst license currently available or enabled.
    Js网站开发学习第一天
    Winform开发1
    MySql安装
    Windows ping加时间戳
    XML特性总结
    linux手册中函数名后小括号中数字的含义
    TCP通信
    swap交换分区概念
  • 原文地址:https://www.cnblogs.com/izcat/p/11721073.html
Copyright © 2011-2022 走看看