鞍山市网站建设_网站建设公司_AJAX_seo优化
2026/1/3 19:33:43 网站建设 项目流程

题目链接

洛谷 P2904 [USACO08MAR] River Crossing S

F1:背包 DP

首先观察题目条件。由于需要 \(n\) 头奶牛全部过河,求最小时间,所以可以认为假设有 \(k\) 次过河,第 \(i\) 次过河过 \(a_i\) 头奶牛,那么 \(a_1+a_2+\dots +a_k=n\)。所以可以看成有 \(n\) 件物品,表示本次过河带几头奶牛,如果以奶牛数量为代价,所需时间为价值,则第 \(i\) 件物品代价为 \(i\),价值为 \(2M+M_1+M_2+\dots+M_i\),即过河时间加上一来一回。

所以设 \(dp_j\) 表示 \(j\) 头奶牛过河同时约翰把木筏划回来的最少时间。由于每次带几头奶牛是任意的,所以是完全背包,状态转移方程如下,其中 \(i\) 枚举“物品”,\(j\) 枚举奶牛头数,\(s_i=M_1+M_2+\dots+M_i\),用前缀和求即可。

\[s_i=s_{i-1}+M_i\\ dp_j=\min{(dp_j,dp_{j-i}+s_i+2M)} \]

代码如下,时间复杂度 \(O(n^2)\),具体细节与普通完全背包类似。

#include<bits/stdc++.h>
using namespace std;const int N=2505;
int n,m;
int a[N],s[N],dp[N];int main(){scanf("%d%d",&n,&m);for (int i=1;i<=n;++i) scanf("%d",a+i);for (int i=1;i<=n;++i) s[i]=s[i-1]+a[i];memset(dp,0x3f,sizeof dp);dp[0]=0;for (int i=1;i<=n;++i){for (int j=i;j<=n;++j) dp[j]=min(dp[j],dp[j-i]+s[i]+2*m);}printf("%d",dp[n]-m);return 0;
}

F2:区间 DP

假设原本有 \(x\ (x>1)\) 头奶牛一起过江,那么除了让它们一起过江,还可以把它们分成前 \(y\) 头奶牛,后 \(x-y\) 头奶牛分别过江。假设 \(i\) 头奶牛过江所需最少时间为 \(dp_i\),那么如果不分开,所需时间为 \(dp_x\);如果按上述方案分开过江,就变为 \(dp_y\)\(dp_{x-y}\) 两个子问题,所需时间为分别所需时间 \(dp_y,dp_{x-y}\) 再加上船空载一程所需时间 \(M\)。综上所述,状态转移方程如下,其中 \(i\) 枚举奶牛头数,\(j\) 枚举割点,即前一部分奶牛头数。

\[dp_i=\min{(dp_i,dp_j+dp_{i-j}+M)} \]

初始化即为 \(dp_i=\begin{cases}M&,i=0\\M+M_1+M_2+\dots+M_i=dp_{i-1}+M_i&, i\ge1\end{cases}\),用前缀和求得。代码如下,时间复杂度 \(O(n^2)\)

#include<bits/stdc++.h>
using namespace std;const int N=2505;
int n,m;
int a[N],dp[N];int main(){scanf("%d%d",&n,&m);for (int i=1;i<=n;++i) scanf("%d",a+i);dp[0]=m;for (int i=1;i<=n;++i) dp[i]=dp[i-1]+a[i];for (int i=1;i<=n;++i){for (int j=1;j<i;++j) dp[i]=min(dp[i],dp[j]+dp[i-j]+m);}printf("%d",dp[n]);return 0;
}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询