XTU算法专题个人赛3 (DP专题)解题报告

本文为XTU算法比赛的个人赛3的解题报告,重点探讨动态规划(DP)题目。作者反思了在HDU1176、HDU1428、HDU5410、CodeForces 543A和HDU5418等题目中的解题思路和错误,通过BFS、DFS、01背包和完全背包等方法解决问题,并分享了代码实现。
摘要由CSDN通过智能技术生成

写这个报告的目的是提醒自己,自己曾经爆0

第一题 HDU1176 免费馅饼

很容易想到dp[时间][位置]

递归方程很简单,而我为什么没写出来呢。
想想当时我一直卡在怎么存数据哪里,递归什么都想出来了。
为什么卡在存数据哪里
智商······
最后注意一下初始化

AC代码

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int pos[100010][11];
int dp[100010][11];
const int inf = 0x3f3f3f3f;
int maxt(int x,int y)
{
    if(y==0) return max(dp[x][y],dp[x][y+1]);
    if(y==10) return max(dp[x][y],dp[x][y-1]);
    return max(max(dp[x][y],dp[x][y+1]),dp[x][y-1]);
}

int main()
{
    int n;
    while(scanf("%d",&n)&&n){
        memset(pos,0,sizeof(pos));
        for(int i=0,x,y;i<n;i++){
            scanf("%d%d",&x,&y);
            pos[y][x]++;
        }

        memset(dp,0,sizeof(dp));
        for(int i=0;i<=100000;i++) for(int j=0;j<=10;j++) dp[i][j] = -inf;
        dp[0][5] = 0;
        for(int i=1;i<=100000;i++){
            for(int j=0;j<=10;j++){
                dp[i][j] = maxt(i-1,j) + pos[i][j];
            }
        }

        int ans = 0;
        for(int i=0;i<=10;i++) ans = max(ans,dp[100000][i]);
        printf("%d\n",ans);
    }
    return 0;


}

第二道 HDU 1428 漫步校园

这道题说是简单,但是自己总是错在一些小细节上。
并且,当时第三题卡了,心情一直不好。
我觉得这个是不应该的,是个人问题。
这题我当时好好看是会做的。

就是首先BFS一遍把每个点到终点的最远距离记录下来。
然后DFS记忆化搜索时剪支就行了。

下面是代码

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
using namespace std;
long long mp[101][101];
long long dp[101][101];
const int dx[] = {0,0,1,-1};
const int dy[] = {1,-1,0,0};
int vis[51][51];
int n;


struct po
{
    int x,y,v;
    friend bool operator < (const po a,const po b){
        return a.v > b.v;
    }
};
void bfs()
{
    memset(vis,0,sizeof(vis));
    po a = {n-1,n-1,mp[n-1][n-1]};
    vis[n-1][n-1] = 1;
    priority_queue<po> q;
    q.push(a);
    while(!q.empty()){
        po v = q.top();q.pop();
        for(int i=0;i<4;i++){
            int xn = v.x + dx[i];
            int yn = v.y + dy[i];
            if(xn < 0 || yn < 0 || xn >=n || yn >= n) continue;
            if(vis[xn][yn]) continue;
            vis[xn][yn] = 1;
            int vn = v.v + mp[xn][yn];
            po u = {xn,yn,vn};
            mp[xn][yn] = vn;
            q.push(u);
        }
    }
}

long long dfs(long long x,long long y)
{
    long long  ans = 0;
    if(!dp[x][y]){
        for(int i=0;i<4;i++){
            int xn = x + dx[i];
            int yn = y + dy[i];
            if(xn <  0 || yn < 0 || xn >= n || yn >=n) continue;
            if(mp[xn][yn] >= mp[x][y]) continue;
            ans += dfs(xn,yn);
        }

        dp[x][y] = ans;
    }
    return dp[x][y];
}


int main()
{
    while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        for(int i=0;i<n;i++){
            for(int j=0;j<n;j++){
                scanf("%lld",&mp[i][j]);
            }
        }
        bfs();
        dp[n-1][n-1] = 1;
        long long ans = dfs(0,0);
        printf("%lld\n",ans);
    }
    return 0;



}

第三题 HDU 5410 CRB and His Birthday

这道题是我最大的失败,明明知道要把A[i] 和 B[i] 分开,但是为什么一直没想出来

首先得想出来取任何一个物品都会得到一个B
这样的话,我们就可以把A[i] + B[i]看成是一个01背包
然后再对 A[i] 进行完全背包

下面是代码

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;


int a[2020],b[2020],w[2020];
int dp[2020];

int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int m,n;
        scanf("%d%d",&m,&n);
        for(int i=0;i<n;i++) scanf("%d%d%d",&w[i],&a[i],&b[i]);
        memset(dp,0,sizeof(dp));

        for(int i=0;i<n;i++){
            for(int j=m;j>=w[i];j--){
                dp[j] = max(dp[j-w[i]] + a[i] + b[i] ,dp[j]);
            }
        }

        for(int i=0;i<n;i++){
            for(int j=w[i];j<=m;j++){
                dp[j] = max(dp[j],dp[j-w[i]] + a[i]);
            }
        }

        printf("%d\n",dp[m]);
    }
    return 0;

}

第四题 CodeForces 543A Writing Code

这题主要完成一个转换,一开始定义dp[前i个程序员][写了j行代码][犯了k个错]

递推方程就知道很简单了,但是这样会错,还要优化。
仔细一看就知道可以优化成dp[写了j行代码][犯了k个错]

下面是代码

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;


int a[510];
int dp[510][510];

int main()
{
    int n,m,b,mod;
    scanf("%d%d%d%d",&n,&m,&b,&mod);
    memset(dp,0,sizeof(dp));
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    dp[0][0] = 1;

    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            for(int k = a[i];k<=b;k++){
                dp[j][k] += dp[j-1][k-a[i]];
                dp[j][k] %= mod;
            }
        }
    }
    int ans = 0;
    for(int i=0;i<=b;i++){
        ans += dp[m][i] ;
        ans %= mod;
    }
    printf("%d\n",ans);
    return 0;

}

第五题 HDU 5418 Victor and World

这题需要状态压缩,并且用floyd算法先算出每个点到另外一个地方的最小距离,因为你中途经过那些点答案并不需要你输出

#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#define inf 0x1f1f1f1f
using namespace std;


int dp[(1<<17)][20];
int mp[20][20];


int main()
{
    int t;
    scanf("%d",&t);
    while(t--){
        int n,m;
        scanf("%d%d",&n,&m);
        memset(dp,0x3f,sizeof(dp));
        for(int i=1;i<=n;i++){
            mp[i][i] = 0;
            for(int j=1;j<=n;j++){
                if(i!=j) mp[i][j] = inf;
            }
        }
        for(int i=0,x,y,z;i<m;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(mp[x][y]>z) mp[x][y] = mp[y][x] = z;
        }

        dp[1][1] = 0;
        for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
        for(int k=1;k<=n;k++) mp[j][k] = min(mp[j][k] , mp[j][i] + mp[i][k]);

        int tot = (1<<n)-1;
        for(int i=1;i<=tot;i++){
            for(int j=1;j<=n;j++){ // st
                if(i&(1<<(j-1))){
                    for(int k=1;k<=n;k++){
                        if(k==j) continue;
                        if(i&(1<<(k-1))) continue;
                        else dp[i | (1<<(k-1))][k] = min(dp[i | (1<<(k-1))][k] , dp[i][j] + mp[j][k]);
                        //printf("%d\n",dp[i][k]);
                    }
                }
            }
        }

        int ans = inf;
        for(int i=1;i<=n;i++) ans = min(ans, dp[tot][i] + mp[i][1]);
        printf("%d\n",ans);
        }
        return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值