动态规划水题集

对于巨佬而言真的就是水题了,作为一个蒟蒻我还是从水题开始慢慢写吧

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;
}
posted @ 2020-03-15 14:31  Salty_Fish  阅读(120)  评论(0编辑  收藏  举报