某公司校招笔试题 树形递归4维DP 图论 烦人的分类讨论

来源:互联网 发布:唐嫣演技知乎 编辑:程序博客网 时间:2024/06/06 02:59

    上上星期,一哥们找工作ing,参加各种公司的校招宣讲会,然而这次他参加完这个我名字都没听说过的公司的宣讲会后,该公司当场留了两道笔试题,让他们拿回来做,给5天时间,在上周五晚上12点前交上就行ORZ,全国收两百人,起薪35-40w。。。薪水简直不要太诱人,这令我怀疑这公司是做毒品国际贸易的。。。。。why u so diao???他让我帮他做的这两道题目我做了大半天,只会做一道,这是第二道,虽然自己的测试数据都过了,但是还不知道对不对,如果有不对的,希望评论区交流一下,欢迎hack。

____________________________________________________________________________________

正题在这(原稿不见了,凭记忆写):

题目描述:

有N<=1000个房间,互相联通,每个房间都有门通向其他房间,而且每个房间最多有三扇门,现在要安装M<=100个路由器,每个路由器的信号只能穿过一扇门。现在每个房间都有一个权值(1~10),现在要在房间里安装这些路由器,使得有信号的房间的权值和最大,输出这个最大值。

样例很容易读,12345是权值,然后跟N-1条边

sample input

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

sample output

10

解题思路:

这就是一个生成树,而且结点的度最多为3,我用DP的方法做的,就是一个dfs,同时记忆了一下数据。

我dp写了4维,方便实现,其实写两维就够了,额外还需要记录两个状态值。

dp[ i ][ j ]代表以第i点为根的这颗子树用不超过j个路由器,所能得到的最大值,后面的两维记录一下根结点的状态和最多两个(因为度为3,除去father,就剩下最多俩children)与他相邻的叶子结点的状态,状态里只要记录当前这个点是否有路由器、是否被叶子结点的路由器辐射到就行了,然后更新公式都在我下面的注释里,好烦。

最后要注意一下,要从一个度为1或2的起点开始dfs,不能随便找,万一找到一个度为3的当根,就炸了。



#include <iostream>#include <stdio.h>#include <math.h>#include <stdlib.h>#include <string>#include <string.h>#include <algorithm>#include <vector>#include <queue>#include <iomanip>#include <time.h>#include <set>#include <map>#include <stack>using namespace std;typedef long long LL;const int INF=0x7fffffff;const int MAX_N=10000;int N,M;int S[1009];vector<int>G[1009];int dp[1009][102][2][2];//第i个点为根向下组成的树,用不超过j个路由器,k=1选自己,k=0不选自己,l=1代表自己被关联覆盖掉int father[1009];int du[1009];//记录度数,以便从一个度为1的开始int solve(int root,int num,int choose,int cover){    //cout<<"dfs "<<root<<" "<<num<<" "<<choose<<" "<<cover<<endl;    if(dp[root][num][choose][cover]!=-1)return dp[root][num][choose][cover];    if(num==0)return dp[root][num][choose][cover]=0;//0个路由器直接返回0    int Size=G[root].size();    int children_num=0;    int children_ID[2];    for(int i=0;i<Size;i++){        if(father[G[root][i]]==root){            children_ID[children_num++]=G[root][i];        }        if(father[G[root][i]]==0){            father[G[root][i]]=root;            children_ID[children_num++]=G[root][i];        }    }    //cout<<"childnum="<<children_num<<endl;    if(children_num==0){        for(int i=1;i<=M;i++){//用几个路由器            dp[root][i][1][1]=S[root];            dp[root][i][1][0]=S[root];        }    }    else if(children_num==1){        int child=children_ID[0];        for(int i=1;i<=M;i++){//共用几个路由器            //选自己,max=max((选了孩子,没选孩子且孩子被覆盖),没选孩子且孩子没被覆盖)            dp[root][i][1][1]=max(max((solve(child,i-1,1,1)+S[root],solve(child,i-1,0,1)+S[root]),solve(child,i-1,0,0)+S[root]+S[child]),dp[root][i][1][1]);            //不选自己且自己被覆盖,max=(选了孩子且孩子被覆盖)            dp[root][i][0][1]=solve(child,i,1,1)+S[root];            //不选自己且自己不被覆盖,max=(没选孩子且孩子被覆盖,没选孩子且孩子没被覆盖)            dp[root][i][0][0]=max(max(solve(child,i,0,1),solve(child,i,0,0)),dp[root][i][0][0]);        }    }    else{//num==2        int child1=children_ID[0];        int child2=children_ID[1];        for(int i=1;i<=M;i++){//共用i个            //选自己            for(int j=0;j<=i-1;j++){//左边用j个,右边用i-1-j个                //选了左且没选右且右孩子已经被覆盖,选了左没选右且右孩子没被覆盖                int cur1=max(solve(child1,j,1,1)+solve(child2,i-1-j,0,1)+S[root],solve(child1,j,1,1)+solve(child2,i-1-j,0,0)+S[root]+S[child2]);                //选了右且没选左且左孩子已经被覆盖,选了右没选左且左孩子没被覆盖                int cur2=max(solve(child2,i-1-j,1,1)+solve(child1,j,0,1)+S[root],solve(child2,i-1-j,1,1)+solve(child1,j,0,0)+S[root]+S[child1]);                //两边都没选且左孩子被覆盖且右孩子已经被覆盖,两边都没选且左孩子被覆盖且右孩子没被覆盖                int cur3=max(solve(child1,j,0,1)+solve(child2,i-1-j,0,1)+S[root],solve(child1,j,0,1)+solve(child2,i-1-j,0,0)+S[root]+S[child2]);                //两边都没选且左孩子没被覆盖且右孩子已经被覆盖,两边都没选且左孩子没被覆盖且右孩子没被覆盖                int cur4=max(solve(child1,j,0,0)+solve(child2,i-1-j,0,1)+S[root]+S[child1],solve(child1,j,0,0)+solve(child2,i-1-j,0,0)+S[root]+S[child1]+S[child2]);                //两边都选了                dp[root][i][1][1]=max(max(max(max(max(cur1,cur2),cur3),cur4),solve(child1,j,1,1)+solve(child2,i-1-j,1,1)+S[root]),dp[root][i][1][1]);            }            //不选自己            for(int j=0;j<=i;j++){//左边用j个右边用i-j个                //自己被覆盖                //选了左且没选右且右孩子已经被覆盖,选了左没选右且右孩子没被覆盖                int cur1;                if(j>0)cur1=max(solve(child1,j,1,1)+solve(child2,i-j,0,1)+S[root],solve(child1,j,1,1)+solve(child2,i-j,0,0)+S[root]);                else {                    cur1=max(solve(child1,j,1,1)+solve(child2,i-j,0,1),solve(child1,j,1,1)+solve(child2,i-j,0,0));                }                //if(root==3)cout<<"root3cur1"<<cur1<<" child1"<<child1<<endl;                //选了右且没选左且左孩子已经被覆盖,选了右没选左且左孩子没被覆盖                int cur2;                if(i-j>0)cur2=max(solve(child2,i-j,1,1)+solve(child1,j,0,1)+S[root],solve(child2,i-j,1,1)+solve(child1,j,0,0)+S[root]);                else{                    cur2=max(solve(child2,i-j,1,1)+solve(child1,j,0,1),solve(child2,i-j,1,1)+solve(child1,j,0,0));                }                //if(root==3)cout<<"root3cur2"<<cur2<<endl;                //自己没被覆盖                //两边都没选且左孩子被覆盖且右孩子已经被覆盖,两边都没选且左孩子被覆盖且右孩子没被覆盖                int cur3=max(solve(child1,j,0,1)+solve(child2,i-j,0,1),solve(child1,j,0,1)+solve(child2,i-j,0,0));                //两边都没选且左孩子没被覆盖且右孩子已经被覆盖,两边都没选且左孩子没被覆盖且右孩子没被覆盖                int cur4=max(solve(child1,j,0,0)+solve(child2,i-j,0,1),solve(child1,j,0,0)+solve(child2,i-j,0,0));                //两边都选了                int cur5=solve(child1,j,1,1)+solve(child2,i-j,1,1)+S[root];                //if(root==3)cout<<"root3cur5"<<cur5<<endl;                dp[root][i][0][1]=max(max(max(cur1,cur2),cur5),dp[root][i][0][1]);                dp[root][i][0][0]=max(max(cur3,cur4),dp[root][i][0][0]);            }        }    }    return dp[root][num][choose][cover];}int main(){    while(scanf("%d%d",&N,&M)!=EOF){        memset(dp,-1,sizeof(dp));        memset(father,0,sizeof(father));        memset(du,0,sizeof(du));        for(int i=1;i<=N;i++){            G[i].clear();        }        for(int i=1;i<=N;i++){            scanf("%d",&S[i]);        }        for(int i=1;i<=N-1;i++){            int x,y;            scanf("%d%d",&x,&y);            G[x].push_back(y);            G[y].push_back(x);            du[x]++;            du[y]++;        }        int start;        for(int i=1;i<=N;i++){            if(du[i]==1){//从一个度为1的开始dfs                start=i;                father[start]=-1;                break;            }        }        cout<<max(solve(start,M,0,1),solve(start,M,0,0))<<endl;    }    return 0;}


1 1
原创粉丝点击