暑期训练第三周题单完成情况(线性dp、背包问题,区间)

1001方块与收纳盒

通过写几组数据可以发现实际上对于每一个填满i*1的收纳盒的情况为(i-1)*1+(i-2)*1,而且n的最大值才为80,所以直接暴力求出每一种情况,并将它们存入一个数组中。

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n;
int dp[85];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    dp[0]=1,dp[1]=1;
    for(int i=2;i<=80;i++){
        dp[i]=dp[i-1]+dp[i-2];
    }
    int t;
    cin>>t;
    while(t--){
        cin>>n;
        cout<<dp[n]<<endl;
    }
}


1002舔狗舔到最后一无所有

在求出1,2,3,4天可以有多少种点外卖方式后,大胆猜测一个规律 dp[i]=2*(dp[i-1]+dp[i-2]),取余就好。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,t;
int dp[100005];
signed main(){
    dp[1]=3;
    dp[2]=9;
    for(int i=3;i<=100000;i++){
        dp[i]=2*(dp[i-1]%mod+dp[i-2]%mod)%mod;
    }
    cin>>t;
    while(t--){
        cin>>n;
        cout<<dp[n]<<endl;
    }
}
1003可爱の星空

当写了几组数据的时候会发现,是每一次都拆成平均的时候(也就是当前要合并的数字个数最相等),加和最小,所以想出将输入的每一个数字进行/2,如果整除该步骤没有代价,不能整除时,该步骤有一个代价。并加上除完的数的代价,(1和2的代价为0)。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int t;
int n;
int temp(int n){
    if(n==1||n==2) return 0;
    if(n%2==1){
        return temp(n/2)+temp(n/2+1)+1;
    }
    else return temp(n/2)*2;
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>t;
    while(t--){
        cin>>n;
        cout<<temp(n)<<endl;
    }
}
1004数字三角形
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n;
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n;
    int a=1;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=i;j++){
            printf("%4lld",a);
            a++;
        }
        printf("\n");
    }
}
1016[NOIP2001]装箱问题

背包问题,边输入边遍历,实际上就是看当前物品是否要装入,如果装入后的值更大(可能会取出一些之前放入的东西)则选择装入,反之不装。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int v,dp[20005],n;
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>v;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        for(int j=v;j>=0;j--){
            if(x<=j) dp[j]=max(dp[j],dp[j-x]+x);
        }
    }
    cout<<v-dp[v]<<endl;
}
1017[NOIP2005]采药

背包问题,边输入边遍历,实际上就是看当前物品是否要装入,如果装入后的值更大(可能会取出一些之前放入的东西)则选择装入,反之不装。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int v,dp[10005],n;
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>v;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x,y;
        cin>>x>>y;
        for(int j=v;j>=x;j--){
            dp[j]=max(dp[j],dp[j-x]+y);
        }
    }
    cout<<dp[v]<<endl;
}
1018[NOIP2006]开心的金明

背包问题,边输入边遍历,实际上就是看当前物品是否要装入,如果装入后的值更大(可能会取出一些之前放入的东西)则选择装入,反之不装。但本题与前两道不同的是他要的是两者相乘。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,m,x,y;
int dp[30005];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>x>>y;
        for(int j=n;j>=x;j--){
            if(j>=x){
                dp[j]=max(dp[j],dp[j-x]+x*y);
            }
        }
    }
    cout<<dp[n]<<endl;
}
1019CSL分苹果

要得到质量最接近的两堆苹果,其实就是求出可以拼出的质量最接近sum/2的值,但要注意一下,dp的是sum的值所以dp数组要开一个大于100*100的数组。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,a[105],dp[10005];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n;
    int sum=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=sum/2;j>=a[i];j--){
            dp[j]=max(dp[j],dp[j-a[i]]+a[i]);
        }
    }
    cout<<dp[sum/2]<<' '<<sum-dp[sum/2]<<endl;
}
1020失衡天平

谁一个动态转移的方程,每多一个物体,无非就是放左边、放右边或者不选,就是这三种情况,然后每一次记录下这三种情况中的最大值,dp[i][j]表示天平上总共有i个物品,天平左边比天平右边多j。所以如果继续放在左边则是dp[i][j+a[i]]+a[i],放右边则为dp[i][abs(j-a[i])]+a[i](实际上数组下标不能为负,所以当右边大于左边的时候,天平就调换左右),不放则为dp[i-1][j]。

#include<bits/stdc++.h>
using namespace std;
//#define int long long
const int mod=1e9+7;
int n,m,dp[105][10005];
int a[105];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>m;
    int sum=0;
    memset(dp,-0x3f,sizeof(dp));
    dp[0][0]=0;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        sum+=a[i];
    }
    for(int i=1;i<=n;i++){
        for(int j=0;j<=100;j++){
            dp[i][j]=max(dp[i][j],dp[i-1][j]);
            dp[i][j]=max(dp[i][j],dp[i-1][abs(j-a[i])]+a[i]);
            dp[i][j]=max(dp[i][j],dp[i-1][j+a[i]]+a[i]);
        }
    }
    int mm=0;
    for(int i=0;i<=m;i++){
        mm=max(mm,dp[n][i]);
    }
    cout<<mm<<endl;
}
1005花店橱窗

dp按顺序去找每一个花能插入的花瓶,然后进行累加,最后找出最大值。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=1e9+7;
int n,m;
int dp[105][105];
int inf=0x3f3f3f3f;
int a[105][105];
int ans[105];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
            dp[i][j]=-inf;//初值设为负的最大值是因为最后累加后也为负的,所以在设初值的时候要注意
        }
    }
    for(int i=1;i<=n;i++){//当前遍历到哪一朵花
        for(int j=i;j<=m-(n-i);j++){//当前这朵花可以放置的位置
            for(int k=i-1;k<j;k++){//这朵花的前一朵花可以放置的位置
                dp[i][j]=max(dp[i][j],dp[i-1][k]+a[i][j]);//比较当前这朵花放在哪里的时候值最大
            }
        }
    }
    int sum=-inf;
    for(int i=n;i<=m;i++) sum=max(sum,dp[n][i]);//找出最大值
    cout<<sum<<endl;
    int cnt=n;
    for(int i=n;i;i--){
        for(int j=1;j<=m;j++){//从小往大遍历的原因:题干中说输出字典序最小的方式
            if(dp[i][j]==sum){
                ans[cnt--]=j;//才能够后往前找每一朵花应该放置的位置。
                sum-=a[i][j];
                break;
            }
        }
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
    cout<<endl;
}
1009「木」迷雾森林

实际上就是计算从左下角走到右上角到底有几条路可走,遇到障碍物需要绕行,且只能向右或者向上走。

实际上动态方程就是dp[i][j]=dp[i+1][j]+dp[i][j-1](如果当前格子的左边或者下边的格子是障碍物的话,就跳过该点。

#include<bits/stdc++.h>
using namespace std;
//#define int long long//开long long会报超内存
const int mod=2333;
int n,m;
int a[3005][3005];
int dp[3005][3005];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cin>>a[i][j];
        }
    }
    for(int i=n;i>=1;i--){//初始化dp数组,第一列从或往前遍历,如果为零dp[i][1]为一,否则就结束
        if(a[i][1]==1) break;
        else dp[i][1]=1;
    }
    for(int i=1;i<=m;i++){//初始化dp数组,最后一行从前往后遍历,如果为零dp[n][i]为一,否则就结束
        if(a[n][i]==1) break;
        else dp[n][i]=1;
    }
    for(int i=n-1;i>=1;i--){
        for(int j=2;j<=m;j++){
            if(a[i+1][j]==0) dp[i][j]+=dp[i+1][j];//从下边到达当前格子
            if(a[i][j-1]==0) dp[i][j]+=dp[i][j-1];//从左边到达当前格子
            dp[i][j]%=mod;
        }
    }
    cout<<dp[1][m]<<endl;
}
1007[NOIP2002]过河卒

将马所控制的九个点标记出来,然后进行dp,如果当前点的上一个点或者左一个点不是被马所控制的点,那么dp[i][j]+=dp[xx][yy];最后输出dp[n][m]。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=2333;
int x,y,n,m;
int dx[]={1,2,-1,-2,1,-2,-1,2};
int dy[]={2,1,-2,-1,-2,1,2,-1};
map<pair<int,int>,int>mp;
int dp[25][25];
bool temp(int a,int b){
    if(a>=0&&a<=n&&b>=0&&b<=m) return true;
    else return false;
}
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>m>>x>>y;
    dp[0][0]=1;
    mp[make_pair(x,y)]++;
    for(int i=0;i<8;i++){
        mp[make_pair(x+dx[i],y+dy[i])]++;
    }
    for(int i=0;i<=n;i++){
        for(int j=0;j<=m;j++){
            int xx=i-1,yy=j;
            if(temp(xx,yy)&&mp.count(make_pair(xx,yy))==0) dp[i][j]+=dp[xx][yy];
            xx=i,yy=j-1;
            if(temp(xx,yy)&&mp.count(make_pair(xx,yy))==0) dp[i][j]+=dp[xx][yy];
        }
    }
    cout<<dp[n][m]<<endl;
}
1008[NOIP2008]传球游戏

dp问题,但是与普通的dp不同的是它本身是一个环,所以在第一个点和最后一个点上要注意,在第一个点的时候dp[i][j]+=dp[2][j-1]+dp[n][j-1],在最后一个点的时候dp[i][j]+=dp[1][j-1]+dp[n-1][j-1];其余点为dp[i][j]+=dp[i-1][j-1]+dp[i+1][j-1];dp[i][j]代表第j轮的时候遍历到第i个人的可能情况有几种。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod=2333;
int n,k;
int dp[35][35];
signed main(){
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    cin>>n>>k;
    dp[1][0]=1;
    for(int j=1;j<=k;j++){
        for(int i=1;i<=n;i++){
            if(i==1) dp[i][j]+=dp[2][j-1]+dp[n][j-1];
            else if(i==n) dp[i][j]+=dp[1][j-1]+dp[n-1][j-1];
            else dp[i][j]+=dp[i-1][j-1]+dp[i+1][j-1];
        }
    }
    cout<<dp[1][k]<<endl;
}

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值