dp 专题

一大波的dp题目,算是总结一下

基础dp

1.hdu 1284

背包问题

有1,2,3三种硬币,给你数字求它的兑换方法

硬币可以无限取,背包问题

/********************************************
Author         :Crystal
Created Time   :
File Name      :
********************************************/

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
int a[4]={1,2,3};
long long dp[500000];
int main()
{
	freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int n;
	dp[0]=1;
	for(int i=1;i<=3;i++){
		for(int j=i;j<=300000;j--){
			dp[j]+=dp[j-i];
		}
	}
	while(scanf("%d",&n)!=EOF){
		cout << dp[n] << endl;
	}
	return 0;
}
总结:背包问题时,注意每次可以取的物品是是否有限制

没有限制那么就是从1开始,有限制就是从n开始,避免重复取

2.hdu 1059

给你1-6中纸币的个数,问是否可以将这些纸币分为两部分相等的纸币

值得注意的是这道题如果是简单的进行dp,那么会超时,所以要进行二进制优化,get 新姿势!好好理解下

在代码中有注释二进制优化部分

/********************************************
Author         :Crystal
Created Time   :
File Name      :
********************************************/

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
int dp[200000];
int v[10];
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int cnt = 0;
	while(true){
		memset(dp,0,sizeof dp);
		memset(v,0,sizeof v);
		cnt++;
		int flag=0;
		int sum=0;
		for(int i=1;i<=6;i++){
			cin >> v[i];
			flag += v[i];
			sum += v[i]*(i);
		}
		dp[0]=1;
		int x;
		if(flag == 0)break;
		int ans = 0;
		if(sum%2){
			cout << "Collection #" << cnt << ":\nCan't be divided.\n\n";
		}
		else{
			for(int i=1;i<=6;i++){
				for(int j=1;j<=v[i];j*=2){//二进制优化
					x = i*j;
					for(int t=sum/2;t>=x;t--){
						if(dp[t-x]){
							dp[t]=1;
							//cout << t;
						}
					}
					v[i]-=j;
				}
				x =v[i]*i;
				if(x){
					for(int t=sum/2;t>=x;t--){
						if(dp[t-x])dp[t]=1;
					}
				}
			}
			if(dp[sum/2]){
				cout << "Collection #" << cnt << ":\nCan be divided.\n\n";
			}
			else{
				cout << "Collection #" << cnt << ":\nCan't be divided.\n\n";
			}
		}
	}

	
	return 0;
}

hdu 1231

最大连续子序列

经典问题,

if(dp[i-1]>=0){
dp[i]=dp[i-1]+a[i];
}
else dp[i]=a[i];

如果前面的dp[i-1]大于等于零那么就加上前面的dp[i-1]

然后遍历一遍得到最大值

/********************************************
Author         :Crystal
Created Time   :
File Name      :
********************************************/

#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <climits>
#include <string>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <set>
#include <map>
using namespace std;
#define inf 0x3f3f3f3f
int a[100005];
long long b[100005];
long long dp[100005];
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int n;
	while(cin >> n && n){
		b[0]=0;
		for(int i=1;i<=n;i++)dp[i]=0;
		int flag = 0;
		for(int i=1;i<=n;i++){
			cin >> a[i];
			b[i]=b[i-1]+a[i];
			if(a[i]<0)flag++;
		}
		dp[1]=a[1];
		int s = 0;
		int e = 0;
		if(flag == n){
			cout <<0 << ' ' << a[1] <<  ' ' << a[n] << endl;
		}
		else{
			long long nmax = inf*-1;
			for(int i=2;i<=n;i++){
				if(dp[i-1]>=0){
					dp[i]=dp[i-1]+a[i];
				}
				else dp[i]=a[i];
			}
			for(int i=1;i<=n;i++){
				if(dp[i]>nmax){
					nmax = dp[i],e = i;
				}
			}
			long long x = b[e]-nmax;
			//cout << x;
			for(int i=1;i<=n;i++){
				if(b[i]==x){
					s = i;
					break;
				}
			}
			cout << nmax << ' ' << a[s+1] << ' ' << a[e] << endl;
		}
	}
	
	return 0;
}
hdu 1023

概率问题

至少一份的概率就是1减去一份都没有的概率



你可能感兴趣的:(dp)