线性DP(内含 传纸条,编辑距离, 调度问题,徒步旅行,流浪月球,数的计数,舔狗舔到最后一无所有)
流浪月球【第四周】
#include <bits/stdc++.h> using namespace std; typedef long long ll; const ll mod=1e9+7; ll dp[2005],pre[2005]; //dp[i][j]指在前i天恰好选j个的方案数 //pre[i][j]指在前i天选1~j个的方案数 int main() { ll n,ans=0; scanf("%lld",&n); for(ll i=1;i<=n;i++) { //前i天最少选i个 ,最多选1+2+……+i,即i*(i+1)/2 for(ll j = i;j<=min(n,i*(i+1)/2);j++) { if(i==j) { dp[j]=1;//在前i天选i个 ,一种方案 } else { dp[j]=(pre[j-1]-pre[j-i-1])%mod; //如果在前i天选j个,那么在前i-1最多选j-1个,最少选j-i个 //意义:在前i天选j个的方案数等于 在i-1天选j-i个,j-i+1……j-1个 //对应的,在第i天就会选i个,i-1 ……1个 } } ans+=dp[n];//压缩维度 ans%=mod; memset(pre,0,sizeof(pre)); for(ll j = i;j<=n;j++) { pre[j] = pre[j-1]+dp[j]; //在前i天选1~j个的方案数等于在 i天选1~j-1个加上刚好在第i天选j个 } } printf("%lld",ans); return 0; }
徒步旅行(travel)【第三周】
拿到这道题,我首先想得广搜,然后……就没有然后了。听了评奖,我才倏地领悟,居然是DP!
#include<bits/stdc++.h> using namespace std; #define int long long const int MAXN=3e5; struct node{ int x,y,c; node(){x=y=c=0;} node(const int X,const int Y):x(X),y(Y),c(max(X,Y)){} }arr[MAXN+5]; bool cmp(node x,node y) { //按区域从小到大,同一区域按顺时针排列 if(x.c==y.c) { if(x.x==y.x) return x.y>y.y; return x.x<y.x; } return x.c<y.c; } inline int dis(const int i,const int j){return abs(arr[i].x-arr[j].x)+abs(arr[i].y-arr[j].y);}//求两点的曼哈顿距离 int hasind;//总区域数量 int dp[MAXN+5][2]; //在i号区域以 dp[i][0] 左上 dp[i][1] 右下 结尾 的最小花费 int l[MAXN+5],r[MAXN+5];//i区域左上点,右下 点 signed main(){ int N; scanf("%d",&N); for(int i=1,a,b;i<=N;++i){scanf("%d %d",&a,&b);arr[i]=node(a,b);} sort(arr+1,arr+N+1,cmp); l[hasind]=0; for(int i=1,pre=0;i<=N;++i){ if(arr[i].c!=pre) { //如果这两个区域刚好不同,那么前一个是旧的结尾,后一个是新的开始 r[hasind]=i-1; pre=arr[i].c; l[++hasind]=i; } } r[hasind]=N; for(int i=1;i<=hasind;++i){ //对于两个区域分别可以连左上左上,右下左上,右下右下,左上右下 dp[i][0]=min(dp[i-1][0]+dis(l[i-1],r[i]),dp[i-1][1]+dis(r[i-1],r[i]))+dis(r[i],l[i]); dp[i][1]=min(dp[i-1][0]+dis(l[i-1],l[i]),dp[i-1][1]+dis(r[i-1],l[i]))+dis(l[i],r[i]); } printf("%lld\n",min(dp[hasind][0]+dis(l[hasind],0),dp[hasind][1]+dis(r[hasind],0)));//回到原点 return 0; }
调度问题
dp[i][j]表示处理第i个作业且A的总工作时间为j时B的总工作时间
则对于每一个i,如果j<a[i](A的总时间还不能处理i),因为不得不处理,因此只能由B处理
dp[i][j]=dp[i-1][j]+b[i]
否则A,B都可以处理 dp[i][j]=min(dp[i-1][j]+b[i],dp[i-1][j-a[i]])
dp[i-1][j]+b[i]
:如果第i个处理是B做的,那么A的总工作时间不变,因此i-1与i时的j是相等的
dp[i-1][j-a[i]]
:此时是A做,所以B不做,因为此时A的总时间为j,所以上一步的总时间为j-a[i]
最后枚举A的时间,每次取A,B中的较大值
#include<bits/stdc++.h> using namespace std; int n, a[1005], b[1005], ans, sum, dp[1005][1005];//到第i个时j:A的工作时间 dp[i][j]:B的工作时间 int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]),sum+=a[i]; for(int i=1;i<=n;i++) scanf("%d",&b[i]); memset(dp, 0x3f, sizeof(dp)); dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=0;j<=sum;j++){ if(j<a[i]) dp[i][j]=dp[i-1][j]+b[i]; else dp[i][j]=min(dp[i-1][j]+b[i],dp[i-1][j-a[i]]); } } ans = 0x3f3f3f3f; for(int i=0;i<=sum;i++){ if(i < dp[n][i]){ ans = min(ans, dp[n][i]); }else{ ans = min(ans, i); } } printf("%d",ans); return 0; }
编辑距离
题目简述:
题目让我们把字符串B通过插入,删除,修改一个字符三种方式变化为字符A,求最少操作次数。此处求的是最值,考虑用动态规划
定义状态:
dp [ i ] [ j ] 表示使B [ 1 ~ j ] 与 A [ 1 ~ i ]变相等要花的值,最后的答案即为dp [ lenb ] [ lena ]
状态转移:
所有的动态规划题都是从已知推向未知的过程。因此我在思考一个dp时,总是从最后一个阶段着点。 对于此题,首先是边界:
①i==0时,即a为空,那么对应的dp[j][0]的值就为i:减少i个字符,使b转化为a
②j==0时,即b为空,那么对应的dp[0][i]的值就为j:增加j个字符,使b转化为a
if
当A[ i ]==B[ j ]时,dp[ i ][ j ]=dp[ i - 1 ] [ j - 1 ]
( 如果这两位相等,意思是使 i 位与 j 位相等不需要任何代价,只需要计算使 i - 1 位与 j - 1 位相等的代价 )
else
删操作 : 如果删除B [ j ] 这一位,就要使B [ 1 ~ j -1 ]与A [ 1 ~ i ] 匹配. 字符串B的前j-1个字符变为字符串A的前i个需要多少步 (把字符串的第j个字符(最后一个)删除了),删除需要一步因此加1.dp [ i ] [ j ] = dp [ i ] [ j - 1] + 1
插入操作 : 插入就是删除嘛…… 插入一个B [ j + 1],使B [ j + 1 ]匹配A [ i ],那么就要使B [ 1 ~ j ]与A [ 1 ~ i - 1 ] 匹配dp [ i ] [ j ] = dp [ i -1 ] [ j ] + 1;
替换操作 : 把B[ j ]替换成能与A[ i ]匹配的数,字符串A和B的最后两个都相等了,因此都不用再考虑,字符串A的前i-1个字符变为字符串B的前j-1个需要多少步 添加需要一步因此加1, dp[ i ][ j ]=dp[ i - 1 ] [ j - 1 ] + 1
将以上三种情况的最小值作为dp [ i ] [ j ] 的值
#include<bits/stdc++.h> using namespace std; char a[105],b[105]; int lena,lenb,dp[105][105]; int main(){ scanf("%s\n%s",a+1,b+1); lena=strlen(a+1),lenb=strlen(b+1); for(int i=1;i<=lena;i++)dp[i][0]=i; for(int i=1;i<=lenb;i++)dp[0][i]=i; for(int i=1;i<=lena;i++){ for(int j=1;j<=lenb;j++){ if(a[i]==b[j])dp[i][j]=dp[i-1][j-1]; else dp[i][j]=min(dp[i-1][j-1],min(dp[i][j-1],dp[i-1][j]))+1; } } cout<<dp[lena][lenb]; return 0;
传纸条
设f[i][j][k][l]为从小渊传到小轩的纸条到达( i , j ), 从小轩传到小渊的纸条到达( k , l )的路径上取得的最大的好心程度和。
完全可以换一个思路想,即求从给定的起点出发到指定的位置的两条最短严格不相交路线,那么显然,
对于每一步有四种情况:
1.第一张纸条向下传,第二张纸条向下传;
2.第一张纸条向下传,第二张纸条向右传;
3.第一张纸条向右传,第二张纸条向下传;
4.第一张纸条向右传,第二张纸条向右传;
转移方程是:f[i][j][k][l]=max( f[i][j-1][k-1][l] , f[i-1][j][k][l-1] , f[i][j-1][k][l-1] , f[i-1][j][k-1][l] )+a[i][j]+a[k][l]
#include <bits/stdc++.h> using namespace std; int f[55][55][55][55],a[55][55]; int n,m; int main(){ cin >> n >> m; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) cin>>a[i][j]; for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) for (int k=1;k<=n;k++) for (int l=1;l<=m;l++) if(i!=k&&j!=l) f[i][j][k][l]=max(max(f[i][j-1][k-1][l],f[i-1][j][k][l-1]),max(f[i][j-1][k][l-1],f[i-1][j][k-1][l]))+a[i][j]+a[k][l]; cout << f[n][m-1][n-1][m]; return 0; }
我TM我连最基础的dp都不会。。。
少魔芋,多做题
算了,放下面子,一道道记录吧
noip2001 数的计数
定义: f[i] 表示 i 可以生成的数的个数;
状态转移方程: 我们发现 n 在其左边的自然数可为 1 ~ n / 2, 所以i可以生成的个数就等于1~n/2可以生成的所有数的和.
故可列出:
f[i] = f[1] + f[2] + ... + f[n/2] +1;
注: f[1] = 1.
Code
#include <iostream> #include <algorithm> using namespace std; int main() { int n; cin >> n; int f[n + 1]; fill(f + 1, f + n + 1, 1); // 高级 for (int i = 2; i <= n; i++) for (int j = 1; j <= i / 2; j++) f[i] += f[j]; cout << f[n] << endl; return 0; }
LINK:舔狗舔到最后一无所有
参考题解
不能连续三天相等,而每一次又有三种选择。设f[i][j] (j=0/1/2)为第j天选择第i 种的方案总数。
- 如果第j天去了2,j-1天也去了2,那么第j-2 天去0 或1 才能满足条件.
- 如果第j天去了2,j-1天去了0 或 1,就可以满足条件了(因此与j-2无关)
-
f[0][i]=f[1][i-1]+f[2][i-1]+f[1][i-2]+f[2][i-2]
f[1][i]=f[0][i-1]+f[2][i-1]+f[0][i-2]+f[2][i-2]
f[2][i]=f[1][i-1]+f[0][i-1]+f[1][i-2]+f[0][i-2]
- 我们发现第i天去哪一家都是一样的,3家关系是相同的,因此可以缩小至一维
f[i]=f[i-1]*2+f[i-2]*2
-
#include <bits/stdc++.h> #define size 100100 const int MOD=1e9+7; using namespace std; long long dp[size],n,m; int main(){ dp[1]=3,dp[2]=9; for (int i=3;i<size;i++){ dp[i]=(dp[i-1]*2%MOD+dp[i-2]*2%MOD)%MOD; } scanf("%lld",&m); while (m--){ scanf("%lld",&n); printf("%lld\n",dp[n]); } system("pause"); return 0; }
本文来自博客园,作者:Doria_tt,转载请注明原文链接:https://www.cnblogs.com/pangtuan666/p/16598215.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现