dfs,,类似树的想法

P - FatMouse and Cheese

  HDU - 1078 

看了是dfs。但是自己写的时候没有按照那个写。。

而且题目还没看好。他是只能水平或垂直,不能再转向了。。

别人ac代码:

#include <stdio.h>
#include <string.h>
#include <algorithm>
using namespace std;

int n,k,dp[105][105],a[105][105];
int to[4][2] = {1,0,-1,0,0,1,0,-1};

int check(int x,int y)
{
    if(x<1 || y<1 || x>n || y>n)
        return 1;
    return 0;
}

int dfs(int x,int y)
{
    int i,j,l,ans = 0;
    if(!dp[x][y])
    {
        for(i = 1; i<=k; i++)
        {
            for(j = 0; j<4; j++)
            {
                int xx = x+to[j][0]*i;
                int yy = y+to[j][1]*i;
                if(check(xx,yy))
                    continue;
                if(a[xx][yy]>a[x][y])
                    ans = max(ans,dfs(xx,yy));
            }
        }
        dp[x][y] = ans+a[x][y];
    }
    return dp[x][y];
}

int main()
{
    int i,j;
    while(~scanf("%d%d",&n,&k),n>0&&k>0)
    {
        for(i = 1; i<=n; i++)
            for(j = 1; j<=n; j++)
                scanf("%d",&a[i][j]);
        memset(dp,0,sizeof(dp));
        printf("%d\n",dfs(1,1));//他的dp[i][j]表示从(ii,j)走得到的maxx,算是一种逆向思维吧,而且因为dfs(1,1)是确定的。这更像是一种树的结构。
    }

    return 0;
}
自己的TLE代码。  没有什么参考价值吧。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>
#include<cstring>
#include<iomanip>
#include<iostream>
#include<stack>
#include<cmath>
#include<map>
#include<vector>
#define ll long long
#define inf 0x3f3f3f3f
#define bug1 cout<<"bug1"<<endl;
#define bug2 cout<<"bug2"<<endl;
#define bug3 cout<<"bug3"<<endl;
using namespace std;
const int maxn=105;
int a[maxn][maxn];
int dp[maxn][maxn];
int n,k;
void dfs(int x,int y){//对于每一个点都找出它的上一个。
    int tmpu=max(0,x-k),tmpd=min(n,x+k+1);
    for(int i=tmpu;i<tmpd;++i){
        int tmpl=max(y-k,0),tmpr=min(n,y+k+1);
        for(int j=tmpl;j<tmpr;++j){
            if(abs(x-i)+abs(y-j)<=k){
                if(dp[i][j]<dp[x][y]+a[i][j]&&a[i][j]>a[x][y]){
                    dp[i][j]=dp[x][y]+a[i][j];
                    dfs(i,j);
                }
                /*
                if(dp[i][j]+a[x][y]>dp[x][y]&&a[i][j]<a[x][y]){
                    dfs(i,j);
                    dp[x][y]=dp[i][j]+a[x][y];
                }
                */
                /*  反正由前一个状态到这个状态是不可以的
                if(dp[i][j]+a[x][y]>dp[x][y]){
                    dp[x][y]=dp[i][j]+a[x][y];
                }
                */
                /*
                if(dp[i][j]<dp[x][y]+a[i][j]&&a[i][j]>a[x][y]){
                    dp[i][j]=dp[x][y]+a[i][j];
                }*///有当前推后面也不可以
            }
        }
    }
}
void init(){
    memset(dp,0,sizeof(dp));
}
int main(){
    while(~scanf("%d%d",&n,&k)&&n!=-1&&k!=-1){
        init();
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                scanf("%d",&a[i][j]);
            }
        }
        /*
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                cout<<a[i][j]<<' ';
            }
            cout<<'\n';
        }*/
        dp[0][0]=a[0][0];
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                dfs(i,j);
            }
        }
        for(int i=n-1;i>=0;--i){
            for(int j=n-1;j>=0;--j){
                dfs(i,j);
            }
        }
        /*
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                dfs(i,j);
            }
        }
        for(int i=n-1;i>=0;--i){
            for(int j=n-1;j>=0;--j){
                dfs(i,j);
            }
        }*/
        int maxx=0;
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                maxx=max(maxx,dp[i][j]);
            }
        }
        /*
        for(int i=0;i<n;++i){
            for(int j=0;j<n;++j){
                cout<<dp[i][j]<<' ';
            }
            cout<<'\n';
        }*/
        cout<<maxx<<'\n';
    }
}



<think>首先,用户要求使用中文回答问题。主题是“形背包”,这听起来像是一个算法问题,可能指的是形背包问题(Tree Knapsack Problem),通常在动态规划的上下文中讨论。 用户的问题格式要求:回答完问题后,必须提出3-5个相关问题,并以“§§相关问题§§”开头,然后列出问题。禁止换行分散,所以所有内容必须紧凑。 回顾示例格式: - 先回答问题。 - 然后输出代码块(如果有代码)。 - 最后以“§§相关问题§§”开头,列出3-5个问题。 用户的问题是“形背包”,所以我需要解释形背包问题是什么。 形背包问题通常是指在结构上进行背包问题,其中每个节点有重量和价值,我们需要选择节点子集,满足重量约束,最大化价值,同时选择的节点必须满足形约束(如父子关系)。 在回答中,我应该: 1. 简要解释形背包问题。 2. 可能提供一个伪代码或代码示例(如Python),因为示例中有代码块。 3. 然后提出相关问题。 确保回答用中文。 结构: - 开头:解释形背包问题。 - 代码块:提供Python实现。 - 然后:“§§相关问题§§”后跟3-5个相关问题。 相关问题必须基于用户的问题和我的回答。用户的问题是“形背包”,所以相关问题可以涉及算法的变体、应用、优化等。 列出相关问题想法: 1. 形背包问题与普通背包问题的区别? 2. 如何优化形背包问题的解决方案? 3. 形背包问题在实际应用中的例子? 4. 形背包问题的时间复杂度是多少? 5. 是否有其他形动态规划问题? 我需要3-5个。 现在,构建回答。 首先,解释形背包问题: 形背包问题是一种动态规划问题,应用于形结构。每个节点有重量和价值,目标是在满足背包重量约束的前提下,选择节点子集以最大化总价值,同时选择的节点必须满足形约束(如:如果一个节点被选择,则其父节点必须被选择;或反之)。 标准形背包通常是:在中选择一个连通子集,满足重量约束,最大化价值。 在代码中,我可以用Python实现一个简单的版本。 伪代码或代码: - 定义节点。 - 使用DFS遍历。 - 维护dp[u][w]表示在节点u的子中,重量不超过w时的最大价值。 但为了简单,提供一个基本框架。 写一个Python函数。 例如: ```python def tree_knapsack(root, capacity): # dp[u][w] = max value in subtree u with weight w # 使用DFS # 初始化dp # 遍历子节点 # 合并结果 pass ``` 但需要具体实现。 为了符合示例,我可以写一个简化版本。 示例中快速排序有完整代码。 所以,我可以写: ```python class Node: def __init__(self, weight, value, children=None): self.weight = weight self.value = value self.children = children if children else [] def tree_knapsack(root, capacity): # dp[u][w] for node u and weight w # 使用DFS后序遍历 dp = {} def dfs(u): # 初始化dp[u]为一个列表,长度为capacity+1,初始值为0 dp[u] = [0] * (capacity + 1) # 对于当前节点,考虑是否选择它 # 先处理子节点 for child in u.children: dfs(child) # 合并子节点的dp # 现在,对于每个重量,更新包括当前节点的值 # 但标准做法是:dp[u][w] = max over children combinations # 简化:假设是二叉类似 # 更简单:使用二维数组,但这里简化 pass # 完整实现较复杂,省略细节 dfs(root) return max(dp[root]) ``` 但在回答中,我可以提供一个概念性代码。 为了节省时间,我可以写一个基本框架。 现在,回答内容: 1. 解释:形背包问题是一种在形数据结构上解决的背包问题变体,常用于动态规划。每个节点有重量和价值,目标是选择节点子集,满足重量约束并最大化价值,同时选择的节点必须形成中的连通分量(通常包括根节点)。 2. 代码:提供Python代码示例。 3. 相关问题。 最后,输出“§§相关问题§§”后跟3-5个问题。 用中文写。 完整回答草稿: 形背包问题是一种动态规划算法,用于在形结构中选择节点子集,以在满足背包重量限制的情况下最大化总价值。典型实现使用深度优先搜索(DFS)遍历,并为每个节点维护一个动态规划表。 ```python class TreeNode: def __init__(self, weight, value, children=None): self.weight = weight self.value = value self.children = children or [] def tree_knapsack(root, capacity): # 定义一个DP表,key为节点,value为列表dp[w]表示重量不超过w时的最大价值 dp_table = {} def dfs(node): # 初始化当前节点的DP数组,长度为capacity+1,初始0 dp = [0] * (capacity + 1) # 如果节点重量超过capacity,则不可选,但这里简化 # 先处理子节点 for child in node.children: dfs(child) # 合并子节点的DP:这里需要分组背包逻辑 # 简化:假设独立,但实际需要嵌套循环 new_dp = dp[:] # 复制当前 for w in range(capacity, -1, -1): # 更新包括子节点 pass # 省略细节 # 更新包括当前节点 for w in range(capacity, node.weight - 1, -1): if dp[w] < dp[w - node.weight] + node.value: dp[w] = dp[w - node.weight] + node.value dp_table[node] = dp dfs(root) return max(dp_table[root]) ``` 注意:上述代码是概念性的,完整实现需要处理子节点的合并,通常使用分组背包或类似方法。 §§相关问题§§ 1. 形背包问题与普通背包问题的主要区别是什么? 2. 如何优化形背包问题的空间复杂度? 3. 形背包问题在计算机科学中的实际应用有哪些? 4. 对于大型形背包问题有哪些高效解法? 5. 形背包问题的时间复杂度通常是多少? 确保问题数量在3-5个,我这里写了5个。 现在,输出时,确保“§§相关问题§§”在第一行,然后数字列表。</think>形背包问题是一种动态规划算法,应用于形数据结构(如或图),目标是在满足背包重量约束的前提下,选择节点子集以最大化总价值。每个节点有重量和价值,选择的节点通常需满足形约束(如连通性)。核心思想是通过深度优先搜索(DFS)遍历,并为每个节点维护一个动态规划表(DP表),其中`dp[u][w]`表示在节点`u`的子中,重量不超过`w`时的最大价值。实现时需处理子节点合并,常用分组背包逻辑。 ```python class TreeNode: def __init__(self, weight, value, children=None): self.weight = weight self.value = value self.children = children or [] # 子节点列表 def tree_knapsack(root, capacity): # 初始化DP表:字典,key为节点,value为长度capacity+1的列表 dp_table = {} def dfs(node): # 创建当前节点的DP数组,初始化为0(重量0时价值0) dp = [0] * (capacity + 1) # 如果当前节点重量合法,则更新其自身(不选子节点的情况) if node.weight <= capacity: for w in range(node.weight, capacity + 1): dp[w] = node.value # 遍历子节点并合并DP(分组背包逻辑) for child in node.children: dfs(child) # 递归处理子节点 child_dp = dp_table[child] # 临时数组用于合并 new_dp = dp[:] # 复制当前状态 for w in range(capacity, -1, -1): # 倒序更新避免覆盖 for cw in range(w + 1): # 尝试分配重量cw给子节点,剩余给其他 if cw <= w and new_dp[w] < dp[w - cw] + child_dp[cw]: new_dp[w] = dp[w - cw] + child_dp[cw] dp = new_dp # 更新DP dp_table[node] = dp # 存储当前节点DP dfs(root) # 从根节点开始DFS return max(dp_table[root]) # 返回根节点最大价值 # 示例用法 if __name__ == "__main__": # 构建:根节点重量2、价值5,子节点重量1、价值3 child = TreeNode(1, 3) root = TreeNode(2, 5, [child]) capacity = 3 print(tree_knapsack(root, capacity)) # 输出最大价值,如8(选择根和子节点) ``` **注意**:上述代码为简化实现,实际中需优化边界处理(如重量为0的情况)和空间复杂度(例如使用滚动数组)。时间复杂度通常为O(n * capacity²),其中n为节点数。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值