pat1053Path of Equal Weight (30)

56 篇文章 0 订阅

题意分析:

(1)给出一颗树的父子表示关系,给出每一个节点自身编号和节点权值,然后给出每一个非叶子结点及其子节点的邻接关系,然后找出一条路径使得路径上节点的权值之和等于给定的值,若有多条路径,则按照序列的大小关系输出,序列的大小关系指的是:序列a>序列b,则从序列a和序列b的第一个元素开始,第一次出现在同等位置上的a的元素大于b的元素

(2)使用DFS从根节点开始遍历,然后记录下当前的非叶子节点中权值最大的节点,并统计总权值,这样就能保证全局非递减序的输出。

判断策略如下:

①若当前总权值>给定值,则直接退出递归

②若当前总权值=给定值,且是叶子结点,则输出

③若当前总权值<给定值,且不是叶子结点,则找到其子节点中权值最大的节点,最为下一次递归的根节点

可能坑点:

(1)这题的难点在于如何对最后的多条路径输出结果按序输出,可以先存路径,然后再排序,也可以直接在DFS中采用“先大后小”的顺序递归。

(2)递归返回时记得需要将vector序列最后一个元素pop出去,以便进行下一次的操作

参考博客:http://blog.csdn.net/acm_ted/article/details/20801651

#include<cstdio>
#include<cstring>
#include<cmath>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<algorithm>
using namespace std;
#define INF 0x6fffffff
#define MAX 105
vector<int> link[MAX];
vector<int> path;
int weight[MAX];
bool flag[MAX];
int n,m,s;
void dfs(int weit,int idx)
{
    int len;
    weit+=weight[idx];
    if(weit>s) return;//如果权重和大于要求,返回
    else if(weit==s&&link[idx].size()==0)
    {//如果权重和等于要求且当前结点无子树
        path.push_back(weight[idx]);
        printf("%d",path[0]);
        len=path.size();
        for(int i=1;i<len;++i)
            printf(" %d",path[i]);
        printf("\n");
        path.pop_back();
    }
    else if(weit<s&&link[idx].size()>0)
    {
        int maxm,maxIdx=0;
        path.push_back(weight[idx]);
        for(;maxIdx!=-1;)
        {
            maxm=maxIdx=-1;
            len=link[idx].size();
            for(int i=0;i<len;++i)
            {
                if(flag[link[idx][i]]==false&&weight[link[idx][i]]>maxm)
                {
                    maxIdx=link[idx][i];
                    maxm=weight[link[idx][i]];
                }
            }
            if(maxIdx!=-1)
            {
                flag[maxIdx]=true;
                dfs(weit,maxIdx);
            }
        }
        flag[idx]=true;
        path.pop_back();
    }
    return;
}
int main()
{
    int a,b,k;
    scanf("%d%d%d",&n,&m,&s);
    memset(flag,false,sizeof(flag));
    for(int i=0;i<n;++i)
        scanf("%d",weight+i);
    for(int i=0;i<m;++i)
    {
        scanf("%d%d",&a,&k);
        for(int j=0;j<k;++j)
        {
            scanf("%d",&b);
            link[a].push_back(b);
        }
    }
    dfs(0,0);
    return 0;
}

解法二:思路和以上有一处不一样,不是采用DFS,而是直接采用树的父子关系这个特性,每次输入新的子节点时,记录其当前的总权值=父节点的总权值+子节点权值,然后遍历所有的子节点,查看其总权值是否等于给定值,最后对多个序列排序

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

vector<int>path[101];
int total[101],weights[101],father[101]={-1};
bool isLeaf[101];
bool cmp(vector<int>a,vector<int>b)
{
    for(int i=0;i<a.size()&&i<b.size();i++)
    {
        if(a[i]!=b[i])return a[i]>b[i];
    }
}
int main()
{
    int N,M,S;
    scanf("%d%d%d",&N,&M,&S);
    int non_Leaf,childNum,child;
    for(int i=0;i<N;i++)scanf("%d",weights+i);
    father[0]=0;
    total[0]=weights[0];
    memset(isLeaf,1,sizeof(isLeaf));
    for(int j=0;j<M;j++)
    {
        scanf("%d%d",&non_Leaf,&childNum);
        isLeaf[non_Leaf]=0;
        for(int k=0;k<childNum;k++)
        {
            scanf("%d",&child);
            father[child]=non_Leaf;
            total[child]=total[non_Leaf]+weights[child];
        }
    }
    int index=0;
    for(int i=0;i<N;i++)
    {
        int paraent=i;
        if(isLeaf[i]&&total[i]==S)
        {
            while(paraent!=0)
            {
                path[index].push_back(weights[paraent]);
                paraent=father[paraent];
            }
            path[index].push_back(weights[0]);
            reverse(path[index].begin(),path[index].end());
            index++;
        }
    }
    sort(&path[0],&path[index],cmp);
    for(int i=0;i<index;i++)
    {
        int first=1;
        for(int j=0;j<path[i].size();j++)
        {
            if(first)first=0;
            else printf(" ");
            printf("%d",path[i][j]);
        }
        printf("\n");
    }
    return 0;
}

可惜的是这种算法第二个测试案例没有过。原因暂时还没有找出来,希望知道的人告知一下


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值