动态规划水题集
目录
对于巨佬而言真的就是水题了,作为一个蒟蒻我还是从水题开始慢慢写吧
hdu 2602 Bone Collector
01背包模板题,用这道题写写01背包的几种写法吧
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1100;
int val[maxn];
int pla[maxn];
int dp[maxn][maxn];
int N,V;
void solve()
{
for (int i=1;i<=N;i++)
{
for (int j=0;j<=V;j++)
{
if (j>=pla[i])
dp[i][j]=max(dp[i-1][j],dp[i-1][j-pla[i]]+val[i]);
else
dp[i][j]=dp[i-1][j];
}
}
}
int choose[maxn];//这个是用来记录选择了哪个物品的
int main()
{
int T;
cin>>T;
while (T--)
{
memset(dp,0,sizeof(dp));
cin>>N>>V;
for (int i=1;i<=N;i++) cin>>val[i];
for (int i=1;i<=N;i++) cin>>pla[i];
solve();
cout<<dp[N][V]<<endl;//忘了写endl搞了个PE...
/*int n=N,v=V;这一段就是找寻找物品的,我就注释掉了
while (dp[n][v])一直循环找选择的物品
{
if (dp[n][v]==dp[n-1][v])
choose[n]=0;
else
{
choose[n]=1;
v=v-pla[n];开始忘了减...
}
n--;
}
for (int i=1;i<=N;i++)
cout<<choose[i]<<" ";*/
}
return 0;
}
//滚动数组的代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1100;
int val[maxn];
int pla[maxn];
int dp[maxn];
int N,V;
void solve()
{
for (int i=1;i<=N;i++)
for (int j=V;j>=pla[i];j--)//最开始还多写了个if来判断j是不是大于pla[i],看了看AC代码发现写一起就行了...还是太弱
dp[j]=max(dp[j],dp[j-pla[i]]+val[i]);//标准背包状态转移方程
}
int main()
{
int T;
cin>>T;
while (T--)
{
memset(dp,0,sizeof(dp));//别忘了清0
cin>>N>>V;
for (int i=1;i<=N;i++) cin>>val[i];
for (int i=1;i<=N;i++) cin>>pla[i];
solve();
cout<<dp[V]<<endl;
}
return 0;
}
hdu 1024 Max Sum Plus Plus
真的是除了模板题我都不会啊,这道应该算是我第一个真正的dp题了
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
int dp[maxn];
int num[maxn];
int temp[maxn];
int main()
{
int n,m,t;
while (~scanf("%d %d",&m,&n))
{
memset(dp,0,sizeof(dp));
memset(temp,0,sizeof(temp));
for (int i=1;i<=n;i++)
scanf("%d",&num[i]);
for (int i=1;i<=m;i++)
{
t=-1e9;
for (int j=i;j<=n;j++)
{
dp[j]=max(temp[j-1],dp[j-1])+num[j];
temp[j-1]=t;
t=max(t,dp[j]);
}
}
printf("%d\n",t);
}
return 0;
}
hdu 4576 Robot
或许这才是我自己能够半独立想出来的第一道题?主程序好歹是自己写的,但是的确看了题解而且改了改才写好的。
题意:有一个格子数量为n的环,给m个操作,机器人可能向左可能向右,几率相同为50%,问在l~r之间的概率。
最开始一看,这不用模拟还能咋做?但是指数级的增长速度啊!后面看到这样一句话懂了,第i号格子其实就是只能由 i-w号格子 与i+w号格子得来,那把每个都推一遍就好了啊,就可以优化到n*m的复杂度了。我也不知道我怎么运用了dp的思想,反正多做点题吧。
还有,这道题用了滚动数组的思想,因为下一次所在的位置是这一次决定的,所以要用两个数组来回滚。也就是q[maxn],p[maxn]
最后,这道题有点卡常数,不要用cin,cout
#include <bits/stdc++.h>
using namespace std;
const int maxn=210;
int n,m,l,r;
double p[maxn],q[maxn];
int main()
{
while (scanf("%d%d%d%d",&n,&m,&l,&r)!=EOF && (n||m||l||r) )//正确的结束写法
{
double res=0;
for (int i=0;i<=n;i++) q[i]=p[i]=0;
q[0]=1.0;
while (m--)
{
int tem;
scanf("%d",&tem);
for (int i=0;i<n;i++)
{
if (q[i]>0)
{
int a=(i+tem)%n;
int b=((i-tem)%n+n)%n;//环里面必须这样写
p[a]+=q[i]*0.5;
p[b]+=q[i]*0.5;
}
}
for (int i=0;i<n;i++)
{
q[i]=p[i];
p[i]=0;
}
}
for (int i=l-1;i<r;i++)
res+=q[i];
printf("%.4lf\n",res);
}
return 0;
}
hdu 1159 Common Subsequence
公共最长子序列模板题,理解了背包之后再看也不算太难
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e3+10;
int dp[maxn][maxn];
int main()
{
string a,b;
while (cin>>a>>b)
{
memset(dp,0,sizeof(dp));
for (int i=1;i<=a.length();i++)
{
for (int j=1;j<=b.length();j++)
{
if (a[i-1]==b[j-1])//注意字符数组下标从0开始
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}//状态转移方程
}
cout<<dp[a.length()][b.length()]<<endl;
/*stack<char> v;
int la=a.length(),lb=b.length();
while (dp[la][lb])
{
if (a[la-1]==b[lb-1])
{
v.push(a[la-1]);
la--,lb--;
}
else
{
if (dp[la][lb]==dp[la-1][lb])
la--;
else
lb--;
}
}
while (!v.empty()){cout<<v.top();v.pop();}*/
//用于输出最长公共子序列的,感觉和01背包差不多
}
return 0;
}
hdu 1257 最少拦截系统
按照书里的方法,我把两种DP写法都写一遍吧。
首先,这道题字面意思时求一个序列中单调递减子序列有多少个,等价于求最长上升子序列中元素的个数
- 用最长公共子序列的方法求解
最开始看到这个我都惊了,没想到还能这样。首先把题目给的序列再复制一份,对其中一份从小到大排个序,求它们两个序列的最长公共子序列就行了。woc,还能这样?
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int dp[maxn][maxn];
int p[maxn],pp[maxn];
int main()
{
int n;
while (cin>>n)
{
for (int i=1;i<=n;i++)
{
cin>>p[i];
pp[i]=p[i];
}
sort(p+1,p+n+1);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
{
if (p[i]==pp[j])
dp[i][j]=dp[i-1][j-1]+1;
else
dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
}
}
cout<<dp[n][n]<<endl;
}
return 0;
}
- dp方法求解
这道题的dp不同于01背包,最长公共子序列。上述两种至少它的状态表示还比较容易理解,但是LIS的 f(i) 代表的是以第i个数结尾的最长递增子序列的长度,u1s1不太好想。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int dp[maxn];
int p[maxn];
int main()
{
int n;
while (cin>>n)
{
for (int i=1;i<=n;i++)
{
cin>>p[i];
dp[i]=1;//初始化,自身也算一个序列
}
for (int i=2;i<=n;i++)
for (int j=1;j<i;j++)
if (p[i]>p[j])//状态转移方程
dp[i]=max(dp[i],dp[j]+1);
int ans=dp[max_element(dp,dp+n+1)-dp];
cout<<ans<<endl;
}
return 0;
}
hdu 2018 母牛的故事
这道题要用递推。把每一年未成熟的和成熟的分开来看就很容易找到规律。
问题就出在我知道规律但是代码还是有问题,如果给的初始条件少会使时间线后移...
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e4+10;
int young[65],old[65];
int solve (int n)
{
if (young[n]!=-1&&old[n]!=-1)
return young[n]+old[n];
if (young[n]!=-1)
{
old[n]=solve(n-3);
return old[n]+young[n];
}
if (old[n]!=-1)
{
young[n]=solve(n-1);
return old[n]+young[n];
}
young[n]=solve(n-1);
old[n]=solve(n-3);
return old[n]+young[n];
}
int main()
{
for (int i=0;i<65;i++)
young[i]=old[i]=-1;
old[1]=old[2]=old[3]=old[4]=1;
solve(55);
int n;
while (cin>>n&&n)
cout<<young[n]+old[n]<<endl;
return 0;
}