其他分享
首页 > 其他分享> > salesman,动态规划带一点点贪心。

salesman,动态规划带一点点贪心。

作者:互联网

题目直接链接

分析一下:

  这题题意还是比较明白的(少见的一道中文题),他的意思就是:有这么一个无向图:保证联通且点与点直接有唯一的简单路径(说白了就是棵树,根节点是1),每个节点有一个权值(有正有负)和最多经过的次数(>=2),求从根到根的走法中能拿到的最大权值(每个权值只能拿一次,根没有权值,且不限次数)。

  题意还是这么长。。。不过其实每一句话都是比较通俗的,大家应该都能理解题意。

  既然是一棵树,那就先想一想有关树的东西(不过思维不要僵化,也不一定就用有关树的知识),显然最小生成树,倍增lca啥的没啥用,而且求最优很容易想到dp,但是到底行不行呢,还是要试试嘛。

  我们想一想,这个次数可以限制什么呢?首先你要想到这里回去,就要使用一次停留的次数,如果你还要再去一个儿子,那么你还要多停留一次,同理,去两个就要多停留两次。注意,是多停留,也就是说原先的一次该停还是还是要停的,于是,我们就可以从儿子里面选择次数-1个最优的加进去,可是儿子最优的要哪里来呢,提前处理出来,诶,有感觉了Dfs加Dp,也就是树形Dp(其实这里并不是很纯正的Dp,因为可能会t掉,所以稍有不同)。

  基本的思路有了,那我们来想一下怎么转移状态,当然转移前要先定义好,这个的定义应该很简单:Dp[i]表示加入i之后可以加的最大权值。那Dp[i]=max(0,前次数-1大的儿子的最优之和(有一点点贪心的感觉)+i的权值),貌似还挺好写的,但是怎么找到前次数-1大的儿子的最优呢?这个就是代码能力的问题了。。。这里处理方法很多:可以维护一个堆,然后一个一个push进去(因为要边进行dfs边push,所以加一个参数,重定义一下<就好了),也可以Dfs和处理分开,先dfs,然后再处理,这样就不会出现覆盖前面数据的问题了,还有就是,可以我开一个n的数组,然后边dfs边分配内存,也可以,当然,给每一个节点开一个数组就不太好了,你也开不下。

  好了??其实才一半,还要输出有没有多解呢,先看一个错误代码(这里的数据真的有一点水)

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
int val[maxn];
int cs[maxn];
int Dp[maxn];
int jl[maxn];
bool Dj[maxn];
struct E{
    int to;
    int next;
    int tree;
    E(){
        to=next=0;
        tree=1;
    }
}ed[maxn*2];
int head[maxn];
int tot;
int cfr;
void J(int a,int b){
    tot++;
    ed[tot].to=b;
    ed[tot].next=head[a];
    head[a]=tot;
}
bool Cm(int a,int b){
    return Dp[a]<Dp[b];
}
void Dfs(int a){
    int js=cfr;
    int fr=cfr;
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            js++;
            jl[js]=ed[i].to;
        }
    cfr=js;
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            ed[i%2?(i+1):(i-1)].tree=0;
            Dfs(ed[i].to);
        }
    if(js==fr){
        Dp[a]=max(0,val[a]);
        if(val[a]==0)
            Dj[a]=1;
        return;
    }
    sort(jl+1+fr,jl+js+1,Cm);
    int ans=0;
    bool dj=0;
    int E=fr;
    for(int i=js;i>fr&&js-i+1<=cs[a]-1;i--){
        ans+=Dp[jl[i]];
        dj|=Dj[jl[i]];
        E=i;
    }
    if(ans+val[a]<0)
        return;
    if(!(ans+val[a])){
        Dj[a]=1;
        return;
    }
    else{
        Dp[a]=val[a]+ans;
        Dj[a]=dj;
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
        scanf("%d",&val[i]);
    for(int i=2;i<=n;i++)
        scanf("%d",&cs[i]);
    cs[1]=maxn;
    int js1,js2;
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&js1,&js2);
        J(js1,js2);
        J(js2,js1);
    }
    Dfs(1);
    printf("%d\n",Dp[1]);
    if(Dj[1])
        printf("solution is not unique");
    else
        printf("solution is unique");
    return 0;
}
错的ac代码

这个竟然过了。。。大家可以看看有什么问题,我也不再加注释了

能卡掉它的数据:

5
2 2 2 2
2 2 2 2
1 2
1 3
2 4
2 5

正确:

6

solution is not unique

它的输出:

6

solution is unique

大家可不要学。。。

好了,那我们到底怎么判断有无多解呢?

选择的儿子有多解,它有多解。不选的儿子与选的相同,有多解。选出0来,有多解。

所以代码是?:

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
int val[maxn];
int cs[maxn];
int Dp[maxn];
int jl[maxn];
bool Dj[maxn];
struct E{
    int to;
    int next;
    int tree;
    E(){
        to=next=0;
        tree=1;
    }
}ed[maxn*2];
int head[maxn];
int tot;
int cfr;
void J(int a,int b){
    tot++;
    ed[tot].to=b;
    ed[tot].next=head[a];
    head[a]=tot;
}
bool Cm(int a,int b){
    return Dp[a]<Dp[b];
}
void Dfs(int a){
    int js=cfr;
    int fr=cfr;
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            js++;
            jl[js]=ed[i].to;
        }
    cfr=js;
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            ed[i%2?(i+1):(i-1)].tree=0;
            Dfs(ed[i].to);
        }
    if(js==fr){
        Dp[a]=max(0,val[a]);
        if(val[a]==0)
            Dj[a]=1;
        return;
    }
    sort(jl+1+fr,jl+js+1,Cm);
    int ans=0;
    bool dj=0;
    int E=fr;
    for(int i=js;i>fr&&js-i+1<=cs[a]-1;i--){
        ans+=Dp[jl[i]];
        dj|=Dj[jl[i]];
        E=i;
    }
    if(ans+val[a]<0)
        return;
    if(!(ans+val[a])){
        Dj[a]=1;
        return;
    }
    else{
        Dp[a]=val[a]+ans;
        Dj[a]=dj;
        if(E>fr+1&&Dp[jl[E]]==Dp[jl[E-1]]&&(Dp[jl[E]]||Dj[jl[E]]))
            Dj[a]=1;
        if(!Dp[jl[E]])
            for(int i=E-1;i>fr;i--)
                if(!Dp[jl[i]]&&Dj[jl[i]])
                    Dj[a]=1;
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
        scanf("%d",&val[i]);
    for(int i=2;i<=n;i++)
        scanf("%d",&cs[i]);
    cs[1]=maxn;
    int js1,js2;
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&js1,&js2);
        J(js1,js2);
        J(js2,js1);
    }
    Dfs(1);
    printf("%d\n",Dp[1]);
    if(Dj[1])
        printf("solution is not unique");
    else
        printf("solution is unique");
    return 0;
}
View Code

其实还是有问题,不过那个都过了,这个按理说也能过。。。

问题在哪里,大家仔细研究一下,这个问题有点难发现。。。

好的,最后是最后的代码(终于有注释了):

#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
int val[maxn];
int cs[maxn];
int Dp[maxn];
int jl[maxn];//排序用
bool Dj[maxn];
struct E{
    int to;
    int next;
    int tree;
    E(){
        to=next=0;
        tree=1;
    }
}ed[maxn*2];
int head[maxn];
int tot;
int cfr;
void J(int a,int b){
    tot++;
    ed[tot].to=b;
    ed[tot].next=head[a];
    head[a]=tot;
}
bool Cm(int a,int b){
    return Dp[a]<Dp[b];
}
void Dfs(int a){
    int js=cfr;
    int fr=cfr;
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            js++;
            jl[js]=ed[i].to;
        }
    cfr=js;//先提前“申请空间”。
    for(int i=head[a];i;i=ed[i].next)
        if(ed[i].tree){
            ed[i%2?(i+1):(i-1)].tree=0;
            Dfs(ed[i].to);
        }
    if(js==fr){//叶子节点,特判掉
        Dp[a]=max(0,val[a]);
        if(val[a]==0)
            Dj[a]=1;
        return;
    }
    sort(jl+1+fr,jl+js+1,Cm);
    int ans=0;
    bool dj=0;
    int E=fr;
    for(int i=js;i>fr&&js-i+1<=cs[a]-1;i--){
        ans+=Dp[jl[i]];
        dj|=Dj[jl[i]];//记录有无多解
        E=i;
    }
    if(ans+val[a]<0)//0都不到,直接不走它,也没多解
        return;
    if(!(ans+val[a])){//是0,有多解(走与不走)
        Dj[a]=1;
        return;
    }
    else{//大于0
        Dp[a]=val[a]+ans;
        Dj[a]=dj;
        if(E>fr+1&&Dp[jl[E]]==Dp[jl[E-1]]&&(Dp[jl[E]]||Dj[jl[E]]))//如果儿子有相同的(注意,相同的0还要特殊处理)
            Dj[a]=1;
        if(!Dp[jl[E]])//是0
            for(int i=E-1;i>fr;i--)//找一遍看看有没有多解的0
                if(!Dp[jl[i]]&&Dj[jl[i]])
                    Dj[a]=1;
    }
}
int main(){
    int n;
    scanf("%d",&n);
    for(int i=2;i<=n;i++)
        scanf("%d",&val[i]);
    for(int i=2;i<=n;i++)
        scanf("%d",&cs[i]);
    cs[1]=maxn;
    int js1,js2;
    for(int i=1;i<=n-1;i++){
        scanf("%d%d",&js1,&js2);
        J(js1,js2);
        J(js2,js1);
    }
    Dfs(1);
    printf("%d\n",Dp[1]);
    if(Dj[1])
        printf("solution is not unique");
    else
        printf("solution is unique");
    return 0;
}

 

标签:Dj,int,tot,jl,一点点,maxn,salesman,Dp,贪心
来源: https://www.cnblogs.com/wish-all-ac/p/12632895.html