您的位置:首页 > 其它

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

2016-10-22 18:40 267 查看
    上上星期,一哥们找工作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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐