算法-动态规划刷题

滑雪

dfs,然后再记忆化搜索。因为遍历到一个点之后,遍历上下左右四种情况,把最好的算出来保存,下次就不用算了。

#include "bits/stdc++.h"
using namespace std;
#define maxn 120
#define inf 1234567890
int a[maxn][maxn],ans[maxn][maxn],r,c;
int dix[5]={0,0,-1,1};
int diy[5]={-1,1,0,0};
int dfs(int x,int y){
    if(ans[x][y]){
        return ans[x][y];
    }
    ans[x][y]=1;
    for(int i=0;i<4;i++){
        int xx=x+ dix[i];
        int yy=y+diy[i];
        if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&a[xx][yy]<a[x][y]){
            ans[x][y]=max(ans[x][y],dfs(xx,yy)+1);
        }
    }
    return ans[x][y];
}
int main(){
    cin>>r>>c;
    for(int i=1;i<=r;i++){
        for(int j=1;j<=c;j++){
            cin>>a[i][j];
        }
    }
    int max_ans=0;
    for(int i=1;i<=r;i++){
        for(int j=1;j<=c;j++){
            max_ans=max(max_ans,dfs(i,j));
        }
    }
    cout<<max_ans;
    return 0;
}

挖地雷

本质上就是最长递增子序列。

#include "bits/stdc++.h"
using namespace std;
int head[25],a[25],n,v[25][25],ans[25];
stack<int> s;
int main(){
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
        ans[i]=a[i];
    }
    for(int i=1;i<=n-1;i++){
        for(int j=1;j<=n-i;j++){
            cin>>v[i][i+j];
//            v[i+j][i]=v[i][i+j];
        }
    }
    head[1]=1;
    for(int i=2;i<=n;i++){
        int maxn=0,k;
        for(int j=1;j<i;j++){
            if(v[j][i]){
                if(maxn<ans[j]){
                    maxn=ans[j];
                    k=j;
                }
            }
        }
        ans[i]=maxn+a[i];
        if(maxn==0){
            head[i]=i;
        }else{
            head[i]=k;
        }
    }
    int res=0,resx=0;
    for(int i=1;i<=n;i++){
        if(ans[i]>res){
            res=ans[i];
            resx=i;
        }
    }
    int i;
    for(i=resx;head[i]!=i;i=head[i]){
        s.push(i);
    }
    s.push(i);
    while(!s.empty()){
        cout<<s.top()<<" ";
        s.pop();
    }
    cout<<endl;
    cout<<res;
    return 0;
}

最长食物链计数

一开始会错意了,以为和上题一样计算最长的食物链有多长。但它的意思是计算最长的食物链有多少条。何为最长食物链,起点是最佳生产者,终点是最佳消费者,这样的食物链有多少条。因为要判断这两者,所以需要记录入度和出度。计算到达最佳消费者的路径有多少条,是由能够到达它的前面结点所决定,即前面结点的路径数之和,而前面路径又由更前面的结点决定,因此最终追溯到最佳生产者。从起点出发,可以到达的结点加1,入度减1,将入度为0的点加入队列,删除结点,以此类推,就联想到拓扑排序。
拓扑排序就是两个结点间一定有一条路径,并且前一结点在图中一定在后一结点前面。
比如计算机专业应修课程,遍历入度为0的点,就是第一学期的课程,不需要任何基础,然后遍历队列中的点,就要修了先修课才能上,这样形成一个序列。

#include "bits/stdc++.h"
using namespace std;
#define ll long long
#define maxl 5005
int n,v[maxl][maxl],ru[maxl],chu[maxl],m,ans,a,b,f[maxl];
queue<int> q;
int main(){
    cin>>n>>m;
    for(int i=1;i<=m;i++){
        cin>>a>>b;
        v[a][b]=1;
        chu[a]++;
        ru[b]++;
    }
    for(int i=1;i<=n;i++){
        if(ru[i]==0){
            f[i]=1;
            q.push(i);
        }
    }
    while(!q.empty()){
        int a=q.front();
        q.pop();
        for(int k=1;k<=n;k++){
            if(v[a][k]==0){
                continue;
            }
            f[k]+=f[a];
            f[k]%=80112002;
            ru[k]--;
            if(ru[k]==0){
                if(chu[k]==0){
                    ans+=f[k];
                    ans%=80112002;

                }
                q.push(k);
            }
        }
    }
    cout<<ans;
    return 0;
}

采药

最普通的01背包问题
以前解背包问题都是用二维数组。

#include "bits/stdc++.h"
using namespace std;
#define maxn 1005
int t,m,weigth[105],value[105],a[105][maxn];
int main(){
    cin>>t>>m;
    for(int i=1;i<=m;i++){
        cin>>weigth[i]>>value[i];
    }

    for(int i=1;i<=m;i++){
        for(int j=1;j<=t;j++){
            if(weigth[i]<=j){
                a[i][j]= max(a[i-1][j],
                             a[i-1][j-weigth[i]]+value[i]);
            }else{
                a[i][j]=a[i-1][j];
            }
        }
    }
    cout<<a[m][t];
    return 0;
}

这次发现完全可以用滚动数组来节省空间,但是一维数组需要注意第二层循环是从后往前的,因为01背包不能只能加一次,这也是与下面完全背包问题的区别。

#include "bits/stdc++.h"
using namespace std;
int w[105], val[105];
int dp[1005];
int main()
{
    int t,m,res=-1;    
    scanf("%d%d",&t,&m);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&w[i],&val[i]);
    }
    for(int i=1;i<=m;i++) 
    {
        for(int j=t;j>=0;j--) 
        {
            if(j>=w[i])
            {
                dp[j]=max(dp[j-w[i]]+val[i], dp[j]);
            }
        }
    }    
    printf("%d",dp[t]);
    return 0;
}

疯狂的采药

以前专业课学习的时候都是用二维数组,也没考虑优化什么的,这次用二维数组直接超出空间,只能用滚动数组。因为完全背包可以被重复添加,所以从前往后遍历。原来二维数组的k,表示可以买一件或多件,这里不需要担心w[i]是否需要整数倍添加,因为w[i]-2*w[i]之间的值,结果和w[i]的值一样。

#include "bits/stdc++.h"
using namespace std;
#define maxn 10005
#define maxt 10000005
#define ll long long
ll t,m,value[maxn],weigth[maxn], a[maxt];
int main(){
    cin>>t>>m;
    for(int i=1;i<=m;i++){
        cin>>weigth[i]>>value[i];
    }
    for(int i=1;i<=m;i++){
        for(int j=weigth[i];j<=t;j++){
            a[j]= max(a[j],a[j-weigth[i]]+value[i]);
        }
    }
    cout<<a[t];
    return 0;
}

5 倍经验日

#include "bits/stdc++.h"
using namespace std;
#define maxn 1005
int n,x,lose[maxn],win[maxn],w[maxn],a[maxn][maxn],dp[maxn];
int main(){
    cin>>n>>x;
    for(int i=1;i<=n;i++){
        cin>>lose[i]>>win[i]>>w[i];
    }
    for(int i=1;i<=n;i++){
        // 当药物为0的时候,也需要加上lose的值,之前错就在这地方
        for(int j=0;j<=x;j++){
            if(w[i]<=j){
                a[i][j]= max(a[i-1][j]+lose[i],
                             a[i-1][j-w[i]]+win[i]);
            }else{
                a[i][j]=a[i-1][j]+lose[i];
            }
        }
    }
//    for(int i=1;i<=n;i++){
//        for(int j=x;j>=0;j--){
//            if(w[i]<=j){
//                dp[j]= max(dp[j]+lose[i],
//                             dp[j-w[i]]+win[i]);
//            }else{
//                dp[j]=dp[j]+lose[i];
//            }
//        }
//    }
    printf("%lld",5ll*a[n][x]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值