动态规划是一种通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。
动态规划应用于子问题重叠的情况:
- 要去刻画最优解的结构特征;
- 尝试递归地定义最优解的值(就是我们常说的考虑从 转移到 );
- 计算最优解;
- 利用计算出的信息构造一个最优解。
第一道接触的DP无疑是背包DP(最简单的01背包)
由于找不到自己写过得01背包,找了个几乎一摸一样(换了个皮肤)的题。
下面是代码:
#include <iostream>
using namespace std;
const int maxn = 13010;
int n, W, w[maxn], v[maxn], f[maxn];
int main() {
cin >> W >> n;
for (int i = 1; i <= n; i++) cin >> w[i] >> v[i];
for (int i = 1; i <= n; i++)
for (int l = W; l >= w[i]; l--)
if (f[l - w[i]] + v[i] > f[l]) f[l] = f[l - w[i]] + v[i]; //状态方程
cout << f[W];
return 0;
}
01背包dp的状态方程:
wi是第i个物品的重量(所占空间),vi是第i个物品的价值
其中 f(i,j)指的是将i个物体放到容量j的空间里所能取得的最大值。
假设前i-1个状态全部处理完毕,在第i布不放入任何东西,那么最大值就是
而如果把第i个物品放进去,那么空间j就会减少wi,相应的增加了vi的价值,这时候就要去找 空间为j-wi 存放物体i-1是的最大值 由此得到最大值为这就是01背包dp的状态方程又来。
数位dp:
这种一般都是板子,用来解决一系列计数问题。
一道周赛题(当时意识到了是数位dp,但是不会写也没有板子)
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
__int64 dfs(__int64 n,__int64 ans,__int64 k)
{
if(n<=0)
return ans;
int p=n%10;
int d=n/10;
while(d)
{
if(d%10==0)
ans=ans+(p+1)*k;
d=d/10;
}
ans=ans+(n/10)*k;
return dfs(n/10-1,ans,k*10);
}
int main()
{
__int64 n,m;
while(~scanf("%I64d%I64d",&n,&m))
{
if(n==-1&&m==-1)
break;
if(n==0)
printf("%I64d\n",dfs(m,0,1)+1);
else
printf("%I64d\n",dfs(m,0,1)-dfs(n-1,0,1));
}
}
接下来是一些写过的杂七杂八dp题
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
using namespace std;
typedef long long ll;
ll dp[40][40];
ll mp[40][40];
int main()
{
memset(dp,0,sizeof(dp));
dp[0][0]=1;
ll x1,y1,x2,y2;
cin>>x1>>y1>>x2>>y2;
mp[x2][y2]=1;
mp[x2+1][y2+2]=1;mp[x2+2][y2+1]=1;mp[x2+2][y2-1]=1;mp[x2+1][y2-2]=1;
mp[x2-1][y2-2]=1;mp[x2-2][y2-1]=1;mp[x2-2][y2+1]=1;mp[x2-1][y2+2]=1;
for(int i=0;i<=x1;i++)
{
for(int j=0;j<=y1;j++)
{
if(mp[i][j]==0)
{
if(i==0&&j!=0)
{
dp[i][j]=max(dp[i][j],dp[i][j-1]);
}
else if(i!=0&&j==0)
{
dp[i][j]=max(dp[i][j],dp[i-1][j]);
}
else if(i==0&&j==0)
{
dp[i][j]=1;
}
else
{
dp[i][j]=max(dp[i][j],dp[i-1][j]+dp[i][j-1]);
}
}
}
}
/*for(int i=0;i<=x1;i++)
{
for(int j=0;j<=y1;j++)
cout<<mp[i][j]<<' ';
cout<<endl;
} for(int i=0;i<=x1;i++)
{
for(int j=0;j<=y1;j++)
cout<<dp[i][j]<<"\t";
cout<<endl;
}*/
cout<<dp[x1][y1]<<endl;
}
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
int map[11][11],f[11][11][11][11];
int main()
{
int n;
cin>>n;
int a,b,c;
while(1)
{
cin>>a>>b>>c;
if(a==0&&b==0&&c==0) break;
map[a][b]=c;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=n;k++)
{
int l=i+j-k;
if(l<=0)break;
else
{
f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])));
if(i==k&&j==l)
f[i][j][k][l]+=map[i][j];
else
f[i][j][k][l]=f[i][j][k][l]+map[i][j]+map[k][l];
}
}
}
}
cout<<f[n][n][n][n]<<endl;
}
#include <iostream>
#include <algorithm>
using namespace std;
int map[55][55];
int f[55][55][55][55];
int main()
{
int m,n;
cin>>m>>n;
for(int i=1;i<=m;i++)
for(int j=1;j<=n;j++)
cin>>map[i][j];
for(int i=1;i<=m;i++)
{
for(int j=1;j<=n;j++)
{
for(int k=1;k<=m;k++)
{
int l=i+j-k;
if(l<=0)break;
else
{
f[i][j][k][l]=max(f[i-1][j][k-1][l],max(f[i-1][j][k][l-1],max(f[i][j-1][k-1][l],f[i][j-1][k][l-1])));
if(i==k&&j==l)
f[i][j][k][l]+=map[i][j];
else
f[i][j][k][l]=f[i][j][k][l]+map[i][j]+map[k][l];
}
}
}
}
cout<<f[m][n][m][n]<<endl;
}
#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <string>
#include <queue>
using namespace std;
typedef long long ll;
int a[100005],d1[100005],d2[100005];
queue<int>q;
int ss;
int len1 , len2 ;
int ul(int n)
{
len1 = 1; len2 = 1; //初始长度为1
d1[1] = a[1]; //用于求不上升序列长度
d2[1] = a[1]; //用于求上升序列长度
for (int i=2; i<=n; i++) { //从a[2]开始枚举每个数(a[1]已经加进去了)
if (d1[len1] >= a[i]) d1[++len1] = a[i]; //如果满足要求(不上升)就加入d1
else { //否则用a[i]替换d1中的一个数
int p1 = upper_bound(d1 + 1, d1 + 1 + len1, a[i], greater<int>()) - d1;
d1[p1] = a[i];
}
if (d2[len2] < a[i]) d2[++len2] = a[i]; //同上
else {
int p2 = lower_bound(d2 + 1, d2 + 1 + len2, a[i]) - d2;
d2[p2] = a[i];
}
}
}
int main()
{
int n=0;
int i=0;
while(cin>>a[++n]);n--;
ul(n);
cout<<len1<<endl<<len2<<endl;
}
#include <iostream>
#include <algorithm>
#define maxn 32005
using namespace std;
int n,m;
int v,p,q;
int mw[maxn],mc[maxn],aw[maxn][3],ac[maxn][3];
int f[maxn];
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>v>>p>>q;
if(!q)
{
mw[i]=v;
mc[i]=v*p;
}
else
{
aw[q][0]++;
aw[q][aw[q][0]]=v;
ac[q][aw[q][0]]=v*p;
}
}
for(int i=1;i<=m;i++)
{
for(int j=n;mw[i]!=0&&j>=mw[i];j--)
{
f[j]=max(f[j],f[j-mw[i]]+mc[i]);
if(j>=mw[i]+aw[i][1])
f[j]=max(f[j],f[j-mw[i]-aw[i][1]]+mc[i]+ac[i][1]);
if(j>=mw[i]+aw[i][2])
f[j]=max(f[j],f[j-mw[i]-aw[i][2]]+mc[i]+ac[i][2]);
if(j>=mw[i]+aw[i][1]+aw[i][2])
f[j]=max(f[j],f[j-mw[i]-aw[i][1]-aw[i][2]]+mc[i]+ac[i][1]+ac[i][2]);
}
}
cout<<f[n]<<endl;
}
#include <bits/stdc++.h>
using namespace std;
const int mod = 1000007;
int n, m, a[105], dp[105];
int main() {
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
dp[0] = 1; //边界条件
for(int i = 1; i <= n; i++){ //枚举每一种花
for(int j = m; j >= 1; j--){ //枚举每一个摆花的结束点
for(int k = 1; k <= a[i] && j - k >= 0; k++) //枚举摆花的盆数
dp[j] = (dp[j - k] + dp[j]) % mod;
// cout<<'['<<j<<']'<<dp[j];
}
}
//求和
printf("%d\n", dp[m]); //输出
//for(int i=1;i<=m;i++)cout<<dp[i]<<' ';
return 0;
}
#include<iostream>
using namespace std;
int g[110],f[110],a[110],s[110];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
f[i]=1;
g[i]=1;
}
for(int i=n-1;i>=1;i--)
{
for(int j=i+1;j<=n;j++)
{
if(a[i]>a[j]&&f[i]<=f[j]+1)
{
f[i]=f[j]+1;
}
}
}
for(int i=2;i<=n;i++)
{
for(int j=1;j<i;j++)
{
if(a[i]>a[j]&&g[i]<=g[j]+1)
{
g[i]=g[j]+1;
}
}
}
int maxx=0;
for(int i=1;i<=n;i++)
{
s[i]=f[i]+g[i]-1;
if(s[i]>maxx)
{
maxx=s[i];
}
}
cout<<n-maxx;
}
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
int m,s,t;
cin>>m>>s>>t;
int s1=0,s2=0;
for(int i=1;i<=t;i++)
{
s1+=17;
if(m>=10)
{
m-=10;
s2+=60;
}
else
{
m+=4;
}
if(s2>s1)s1=s2;
if(s1>=s)
{
cout<<"Yes"<<endl<<i<<endl;;
return 0;
}
}
cout<<"No"<<endl<<s1<<endl;
}