【20200513程序设计思维与实践 Week13 作业】

目录

    • D - TT 的苹果树
      • 题意
      • 思路
      • 代码
    • E - TT 的神秘任务3
      • 题意
      • 思路
      • 代码


D - TT 的苹果树

题意

在大家的三连助攻下,TT 一举获得了超级多的猫咪,因此决定开一间猫咖,将快乐与大家一同分享。并且在开业的那一天,为了纪念这个日子,TT 在猫咖门口种了一棵苹果树。

一年后,苹果熟了,到了该摘苹果的日子了。

已知树上共有 N 个节点,每个节点对应一个快乐值为 w[i] 的苹果,为了可持续发展,TT 要求摘了某个苹果后,不能摘它父节点处的苹果。

TT 想要令快乐值总和尽可能地大,你们能帮帮他吗?


思路

此题与“没有上司的舞会”极为相似,利用树型dp求解。

设状态f[i][0]为以i为子树且未选择i节点,f[i][1]为以i为子树且选择了i节点。转移方程为
【20200513程序设计思维与实践 Week13 作业】_第1张图片
其中求和对象是i节点的所有子节点。

实际编程过程中还要对树结构进行处理,得到正确的dp顺序。采用的方法是层次遍历整棵树,以遍历结果的逆序来动归。


代码

#include
#include
#include
using namespace std;

struct node;

struct link{
    link* next;
    int id;
};

struct node{
    int w;
    link* child;
    int pa;

    void add(int v){
        link* p=new link;
        p->id=v;
        p->next=child;
        child=p;
    }

    void clear(){
        link*p,*q;
        p=child;
        while(p!=NULL){
            q=p;
            p=p->next;
            delete q;
        }
        child=NULL;
        pa=-1;
    }
};

node apple[6005];

int f[6005][3];
int N;

struct unit{
    int level;
    int id;

    unit(int l,int i){
        level=l;
        id=i;
    }
};

vector<unit> V; 
queue<int> Q;

void scanTree(){
    V.clear();
    while(!Q.empty())Q.pop();

    int p=1;
    while(apple[p].pa!=-1)p=apple[p].pa;
    int thisL,nextL,cotL;
    thisL=nextL=cotL=0;
    Q.push(p);
    thisL++;
    while(!Q.empty()){
        int thisNode=Q.front();
        Q.pop();
        thisL--;
        link* c=apple[thisNode].child;
        while(c!=NULL){
            Q.push(c->id);
            nextL++;
            c=c->next;
        }
        unit B(cotL,thisNode);
        V.push_back(B);
        if(thisL==0){
            thisL=nextL;
            nextL=0;
            cotL++;
        }
    }
}

int main(){
    while(cin>>N){
        if(N==0)break;
        for(int i=1;i<=N;i++){
            apple[i].clear();
            cin>>apple[i].w;
        }
        int L,K;
        for(int i=1;i<N;i++){
            cin>>L>>K;
            apple[K].add(L);
            apple[L].pa=K;
        }

        scanTree();
        int k=N-1;
        for(;k>=0;k--){
            if(V[k].level!=V[k+1].level)break;
            f[V[k].id][0]=0;
            f[V[k].id][1]=apple[k].w;
        }
        for(;k>=0;k--){
            link* c=apple[V[k].id].child;
            int temp1=0;
            int temp2=0;
            while(c!=NULL){
                temp1+=max(f[c->id][0],f[c->id][1]);
                temp2+=f[c->id][0];
                c=c->next;
            }
            f[V[k].id][0]=temp1;
            f[V[k].id][1]=apple[V[k].id].w+temp2;
        }
        int ans=max(f[V[0].id][0],f[V[0].id][1]);
        cout<<ans<<endl;
    }
}


E - TT 的神秘任务3

题意

TT 猫咖的生意越来越红火,人越来越多,也越来越拥挤。

为了解决这个问题,TT 决定扩大营业规模,但猫从哪里来呢?

TT 第一时间想到了神秘人,想要再次通过完成任务的方式获得猫咪。

而这一次,神秘人决定加大难度。

给定一个环,A[1], A[2], A[3], … , A[n],其中 A[1] 的左边是 A[n]。要求从环上找出一段长度不超过 K 的连续序列,使其和最大。

这一次,TT 陷入了沉思,他需要你们的帮助。


思路

本题使用到了单调队列dp。

题中对答案有如下要求:如果有多个结果,输出起始位置最小的,如果还是有多组结果,输出长度最短的。因此,要对课件中的最大连续字段和问题的解法进行一定的改进。

定义状态f[i]为以i为开头的最大连续字段和,sum[i]为后缀和,f[i]依然为sum[i]-min{sum[k]},但k取值范围为[i+1,i+1+K]。利用滑动窗口降低时间复杂度,将min{sum[k]}所指向的k储存在数组minS[]中,转移方程转化为f[i]=sum[i]-sum[minS[i+1]]

扫描数组f,取最大值、对应元素索引与对应minS即可。


代码

//#include
#include
#include
#include
#include
using namespace std;

int A[200005];
int sum[200005];
int minS[200005];
int f[200005];
int T,N,K;

int que[200005];

struct queue{
    int p;
	int q;
	
	queue(){p=0;q=0;}
    void reset(){
        p=q=0;
    }
	bool empty(){return p==q;}
	int size(){return q-p;}
	void push(int v){que[q++]=v;}
	int front(){return que[p];}
	void pop(){p++;}//前出
	int last(){return que[q-1];}
	void erase(){q--;}//后出
};

queue Q;

void createMin(){
	Q.p=Q.q=0;
	Q.push(1); 
	for(int i=2;i<=K;i++){
		while(!Q.empty()){
			if(sum[Q.last()]>sum[i]) Q.erase();
			else break;
		}
		Q.push(i);
	}
	minS[1]=Q.front();

	for(int i=K+1;i<=2*N;i++){
		if(i-Q.front()>=K) Q.pop(); 
		while(!Q.empty()){
			if(sum[Q.last()]>sum[i]) Q.erase();
			else break;
		}
		Q.push(i);
		minS[i-K+1]=Q.front();
	}
}


int main(){
    scanf("%d",&T);
    while(T--){
        scanf("%d %d",&N,&K);
        for(int i=1;i<=N;i++){
            scanf("%d",&A[i]);
            A[i+N]=A[i];
        }
        int S=0;
        for(int i=2*N;i>0;i--){
            S+=A[i];
            sum[i]=S;
        }

        createMin();
        for(int i=1;i<=N;i++)
            f[i]=sum[i]-sum[minS[i+1]];

        int ans,tagL,tagR,temp;
        ans=f[1];
        if(minS[2]-1>N)temp=minS[2]-1-N;
        else temp=minS[2]-1;
        tagL=1;
        tagR=temp;
        for(int i=2;i<=N;i++){
            if(f[i]>ans){
                ans=f[i];
                if(minS[i+1]-1>N)temp=minS[i+1]-1-N;
                else temp=minS[i+1]-1;
                tagL=i;
                tagR=temp;
            }else if(f[i]==ans){
                if(minS[i+1]-1>N)temp=minS[i+1]-1-N;
                else temp=minS[i+1]-1;
                if(i<tagL){
                    tagL=i;
                    tagR=temp;
                }else if(i==tagL){
                    if(temp<tagR)tagR=temp;
                }
            }
        }
        printf("%d %d %d\n",ans,tagL,tagR);
    }
}

你可能感兴趣的:(【20200513程序设计思维与实践 Week13 作业】)