Fork me on GitHub

线性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;
    }

 

posted @   Doria_tt  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 25岁的心里话
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示