一大波的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减去一份都没有的概率