动态规划的引入

题单:【动态规划1】动态规划的引入 

P1048 [NOIP2005 普及组] 采药

本质上是一道01背包问题

递推式:

dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - tim[i]]+power[i])

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 105;
int T, M;
int tim[maxn];
int power[maxn];
int dp[105][1005];
//定义状态 dp[i][j]为j 为容量为放入前i个物品(按i从小到大的顺序)的最大价值
int main()
{
	cin >> T >> M;//T相当于背包总容量,M相当于物品个数
	for (int i = 1; i <= M; i++)
		cin >> tim[i] >> power[i];
	for (int i = 1; i <= M; i++)
	{
		for (int j =1; j <= T; j++)
		{
			if (tim[i] <= j)//剩余背包容量大于等于物品的重量
			{
				dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - tim[i]]+power[i]);//递推式,装这个物品或不装这个物品
			}
			else
				dp[i][j] = dp[i - 1][j];//只能不装
		 }
	}
	cout << dp[M][T] << "\n";
}

P1616 疯狂的采药

本质上为完全背包问题

递推式为dp[i]=max{dp[j],dp[j-time[i]]+power[i]}

第一层循环为草药个数,第二层循环为采药总时间

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 100005;
int T, M;
int tim[maxn];
int power[maxn];
long long int f[10000005];
//定义状态 dp[i][j]为j 为容量为放入前i个物品(按i从小到大的顺序)的最大价值
int main()
{
	cin >> T >> M;//T相当于背包总容量,M相当于物品个数
	for (int i = 1; i <= M; i++)
		cin >> tim[i] >> power[i];
	for (int i = 1; i <= M; i++)
	{
		for (int j = tim[i]; j <= T; j++)
		{
			f[j] = max(f[j], f[j - tim[i]] + power[i]);
		}
	}
	cout <<f[T] << "\n";
}

P1802 5倍经验日

本质上仍为0,1背包问题只不过当失败是时是仍旧有经验的所以要对递推式稍作修改

递推式:dp[i][j] = max(dp[i-1][j - yao[i]] + win[i], dp[i - 1][j] + lose[i])

#include<iostream>
#include<algorithm>
using namespace std;
const int maxn = 1005;
int n, x;
long long lose[maxn];
long long win[maxn];
long long yao[maxn];
long long dp[maxn][maxn];
int main()
{
	cin >> n >> x;
	for (int i = 1; i <= n; i++)
	{
		cin >> lose[i] >> win[i] >> yao[i];
	}
	for(int i=1;i<=n;i++)
		for (int j = 0; j <= x; j++)
		{
			if (yao[i] <= j)
			{
				dp[i][j] = max(dp[i-1][j - yao[i]] + win[i], dp[i - 1][j] + lose[i]);
			}
			else
				dp[i][j] = dp[i - 1][j] + lose[i];
		}
	cout << 5*dp[n][x] << "\n";
}

P2196 [NOIP1996 提高组] 挖地雷

f[i]:表示加入第i个节点时的最大地雷数

pre[i]:用来记录前驱结点

mp[i][j]用来记录地图上的两个节点是否相连

#include<iostream>
using namespace std;
int mp[25][25];
long long int weight[25];
long long int ans;
int pre[25];//前继结点 
int flag=0;//记录最终到达结点 
long long f[25];
int main()
{
	int n;
	cin>>n;	
	for(int i=1;i<=n;i++)
	   cin>>weight[i];
	for(int i=1;i<=n-1;i++)
	   for(int j=i+1;j<=n;j++)
	      cin>>mp[i][j];
	      
    for(int i=1;i<=n;i++)
    {
       for(int j=1;j<=n;j++)
       {	   
       	    if(mp[j][i]&&f[j]>f[i])
       	    {
       	       f[i]=f[j];
       	       pre[i]=j;
	        }
	   }
	   f[i]+=weight[i];
	   if(f[i]>ans)  {ans=f[i]; flag=i;}
    }
	int node[25];
	int cnt=0;
	int tmp=0;
	node[0]=flag;
	while(1)
	{
		tmp=pre[node[cnt]];
		if(tmp==0) break;
		node[++cnt]=tmp;
	}
	for(int i=cnt;i>=0;i--)
	{
		cout<<node[i]<<" ";
	}
	cout<<"\n";
	cout<<ans<<"\n"; 
	 
}

P4017 最大食物链计数

这是一道拓扑排序题,每次把入度为0的点i入栈,令dp[i]=1然后不断更新和i相邻的点j的dp值,令dp[j]=dp[j]+dp[i],将j的入度-1,若入度为0则入栈,若入栈的点出度为0,则ans+=dp[j]

#include<iostream>
#include<queue>
using namespace std;
int mp[5005][5005];
long long int n,m;
int chu[5005];
int ru[5005];
int dp[5005];
long long ans=0;
queue<int> qu;
int main()
{
   cin>>n>>m;
   for(int i=1;i<=m;i++)
   {
   	int u,v;
   	cin>>u>>v;
   	mp[u][v]=1;
   	chu[u]++;
   	ru[v]++;
   }
   for(int i=1;i<=n;i++)
   {
   	if(ru[i]==0)
   	{
   		qu.push(i);
   		dp[i]=1;
   		
	}
   }
   while(!qu.empty())
   {
   	int temp=qu.front();
   	qu.pop();
   	for(int i=1;i<=n;i++)
   	{
   		if(mp[temp][i]==1)
   		{
   			dp[i]+=dp[temp];
   			dp[i]%=80112002;
   			ru[i]--;
   			if(ru[i]==0)
   			{
   			 qu.push(i);
   			if(chu[i]==0)
   		       {
   				ans+=dp[i];
   				ans%=80112002;
			   }
   			   
   		   }
		}
	}
   }
   cout<<ans<<"\n"; 
	
} 

P1002 [NOIP2002 普及组] 过河卒

一道简单的二维dp,dp[i][j]表示到达(i,j)时最大的路径数

注意当该位置是马所在的点或是马可以到达的点,那么dp[i][j]=0

如果不是则右左边和上边的dp值相加

#include<iostream>
#include<algorithm>
using namespace std;
int n,m;
long long dp[25][25];
int a, b;
int main()
{
	cin >> n >> m >> a >> b;
	for(int i=0;i<=n;i++)
		for (int j = 0; j <= m; j++)
		{
			if (((i - a)*(i-a)+(j - b)*(j-b)) == 5||i==a&&j==b) dp[i][j] = 0;
			else
			{
				if(i==0&&j==0) dp[0][0] = 1;
				if(i == 0&&j!=0) dp[i][j] = dp[i][j-1];
				if(j == 0&&i!=0) dp[i][j] = dp[i-1][j];
				if(i!=0&&j!=0)
				    dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
			}
		}
	cout << dp[n][m]<< "\n";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值