首页 > 试题广场 >

公司管理

[编程题]公司管理
  • 热度指数:1316 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

在小团的公司中,有n位员工。除了最高领导——小团外,每位员工有且仅有一位直接领导。所以,公司内从属关系可以看成一棵树。

现在,公司接到一个项目,需要重新划分这n位员工的从属关系。新的划分描述如下:

1.每个人要么没有下属,要么有至少两个直接下属(即至少有两人的直接领导为这个人)

2.i个人的下属(包括自己)有恰好a_i个。

请注意,直接下属和下属(包括自己)可分别看做树上点的"儿子""子树"

请问是否存在这么一种关系?注意,输入不会给出最高领导的编号。


输入描述:

输入包含多组数据。

对于每组数据,第一行一个整数n,表示公司有n个人。

接下来一行n个数,第i个数为a_i,含义如题面所示。



输出描述:

对每组数据,输出一行"YES"或"NO",代表是否存在这一种从属关系。

示例1

输入

3
1 1 3
2
1 2

输出

YES
NO

说明

对于第一组样例,1和2的直接领导均为3即可

对于第二组样例,无法构造出符合题目要求的关系。注意每个有下属的人至少有2个直接下属。


备注:

对于40%的数据,

对于100%的数据,,数据组数在10以内

#include <stdio.h>
#include <algorithm>
#include <string.h>

using namespace std;

const int maxn = 30;

int n, a[maxn];

bool ans, used[maxn];

void dfs(int t);

void solve(int s, int e, int c, int aa)
{
	if (aa == 1)
	{
		if (c > 1) dfs(e + 1);
		return;
	}
	for (int i = s; i < e; ++i)
	{
		if (a[i] + 1 > aa) break;
		if (used[i]) continue;
		if (i > 0 && a[i] == a[i - 1] && !used[i - 1]) continue;
		used[i] = true;
		solve(i + 1, e, c + 1, aa - a[i]);
		if (ans) return;
		used[i] = false;
	}
}

void dfs(int t)
{
	if (t >= n) 
	{
		ans = true;
		return;
	}
	solve(0, t, 0, a[t]);
}

int main()
{
	while (scanf("%d", &n) != EOF)
	{
		ans = true;
		for (int i = 0; i < n; ++i)
		{
			scanf("%d", &a[i]);
			if (a[i] < 1)
				ans = false;
		}
		sort(a, a + n);
		if (!ans || a[n - 1] != n) 
		{
			printf("NO\n");
			continue;
		}
		int i = 0;
		for (; i < n; ++i)
			if (a[i] > 1) break;
		if (i < n / 2)
		{
			printf("NO\n");
			continue;
		}
		memset(used, false, sizeof(used));
		ans = false;
		dfs(i);
		if (ans) printf("YES\n");
			else printf("NO\n");
	}
	return 0;	
} 

发表于 2021-03-26 23:32:53 回复(0)

根据前面这位朋友牛客633068805 的C++版本而来的java版本。(写注释是个好习惯啊)
简单说下我理解的思路
(1)先用一些值的范围条件过滤一下输入的数,代码注释中的1,2,3,但是3我没想明白为什么(去掉也不影响最终结果);
(2)对节点从小到大排序,最前面的一定是为1的叶子节点;
(3)从第一个非叶子节点开始,从前面的节点中寻找是否存在它的子节点组合(子节点个数cnt>=2);

难点:我觉得这题的难点就在于如何回溯,因为这里的回溯不是简单的单层回溯,涉及到两层:
(1)对于单个节点来说,寻找可能的子节点组合会回溯;
(2)对于整个节点数组来说,也涉及到一层回溯:假如前面的节点A找到了第一个满足条件的子节点组合,但是后面遍历到节点B时找不到了,但其实对于节点A除了找到的第一个还有另外的子节点组合,从这个组合遍历下去可达到最终所有都满足。那么如何从B不满足的状态回溯到A可以继续寻找下一个可能满足组合的状态?

解决办法:我称之为“双环递归”(自己瞎取的名字),我们一般见到的递归都是一个函数,自己调用自己,画个图就是一个环,这里使用了两个函数,dfs可以调用findSum,findSum可以调用调用自身和dfs,画图出来就有两个环。就很好地解决了上面的难点。(这种我也是第一次见,学到了!)

其他细节:
其实findSum就是回溯算法的模板写法,但是这里加了个“树层”去重用来剪枝,如果不加的话会超时。
例子:
[1,1,1,1,3,3,4,8]
找节点4的子节点时,前面第一个3遍历完后不符合,然后遍历到第二个3的时候,前面的3肯定遍历过了,如果相等且used[i-1]=false,说明这个数不能组成有效组合,直接跳过(递归树的同一层去重,这里的作用是剪枝)

ps:关于回溯算法和去重不太熟练的同学可以看这位大佬的题解

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class Main{
    // 节点是否已经被使用,即是否成为了某个节点的子节点
    static boolean[] used;
    static boolean flag;
    static int n;
    static int[] arr;
    public static void main(String[] args) throws IOException {
        Scanner sc = new Scanner(System.in);
        while(sc.hasNext()){
            n = sc.nextInt();
            arr = new int[n];
            used = new boolean[n];
            flag = true;
            for(int i=0;i<n;++i){
                arr[i] = sc.nextInt();
                // 1.不能有<1或2存在
                if(arr[i] < 1 || arr[i]==2){
                    flag = false;
                }
            }
            Arrays.sort(arr);
            // 2.最大的一个数必须为n
            if (!flag || arr[n-1]!=n){
                System.out.println("NO");
                continue;
            }
            int idx=0;
            // 找到第一个非叶子节点的下标
            while(idx<n && arr[idx]==1){
                ++idx;
            }
            // 3.第一个非叶子节点的小标idx>=n/2
            if(idx < n/2){
                System.out.println("NO");
                continue;
            }
            flag = false;
            // 4.如果输入满足以上条件,则可能存在这种关系
            dfs(idx);
            if(flag){
                System.out.println("YES");
            }else{
                System.out.println("NO");
            }

        }
    }

    // idx为当前遍历节点下标,需要找它的子节点
    public static void dfs(int idx){
        if(idx >= n){
            flag = true;
            return;
        }
        // arr[idx]-1是去掉本身
        findSum(0,idx,0,arr[idx]-1);
    }

    // 从[start,end),未使用的节点节点中寻找是否有和为target的且个数cnt>=2的(因为子节点个数>=2)
    public static void findSum(int start,int end,int cnt,int target){
        if(target == 0){
            // 当前节点找到符合条件的子节点,遍历下一个节点
            if(cnt >= 2){
                dfs(end+1);
            }
            return;
        }

        for(int i=start;i<end && arr[i]<=target;++i){
            if(used[i]){
                continue;
            }
            // 剪枝
            if(i>0 && arr[i]==arr[i-1] && !used[i-1]){
                continue;
            }
            used[i] = true;
            // 回溯
            findSum(i+1,end,cnt+1,target-arr[i]);
            used[i] = false;
        }
    }
}
发表于 2022-03-07 17:22:41 回复(0)

import java.util.Scanner;
import java.util.Arrays;
public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while (scanner.hasNext()) {
            int n = scanner.nextInt();
            int[] arr = new int[n];
            boolean flag = true;
            int sum = 0;
            for (int i = 0; i < n; i++) {
                arr[i] = scanner.nextInt();
                sum += arr[i];
                if (arr[i] == 2) {
                    flag = false;
                }
            }
            if (!flag) {
                System.out.println("NO");
                continue;
            }
            sum -= n;
            if (sum == n - 1) {
                System.out.println("YES");
            } else {
                System.out.println("NO");
            }
        }
    }
}

简单说下思路,满足条件1,则每组数据中,不能有值为2(要么没有下属-值为1, 要么2个及以上- 大于2)
满足条件2,则每棵树一共有(a1+a2+...+an-n)条边,由于是一棵树,所以边的个数 = 节点的个数 -1 
发表于 2021-03-09 11:48:13 回复(11)
import java.util.Scanner;
import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        while(in.hasNext()){
            boolean ans = true;
           int n = in.nextInt();
           int[] sort = new int[n];
           for(int i=0;i<n;i++){
                sort[i] = in.nextInt();
            }
            Arrays.sort(sort);
            ArrayList<Integer> list = new ArrayList<>();
            for(int i=0;i<n;i++){
                if(sort[i]==1){
                    list.add(sort[i]);
                }else{
                    ArrayList<Integer> flag = new ArrayList<>();
                    int child = sort[i]-1;
                    int j=list.size()-1;
                     
                    //System.out.println(flag+"flsg");
                    if(!f(list,child,j,flag)){
                       ans = false;
                       break;
                    }
                     
                    for(int k=0;k<flag.size();k++){
                        int location = flag.get(k);
                        list.remove(location);
                        //System.out.println(list+"list");
                    }
                    list.add(sort[i]);
                }
                //System.out.println(list);
            }
            if(!ans){
                System.out.println("NO");
            }else{
                if(list.size()==1){
                    System.out.println("YES");
                }else{
                    System.out.println("NO");
                }
            }
             
                
        }
    }
    public static boolean f(ArrayList<Integer> list,int target,int j,ArrayList<Integer> flag){
        if(j<0){
            if(target!=0){
                return false;
            }else{
                if(flag.size()>=2){
                    return true;
                }
                return false;
            }
        }
        if(list.get(j)==target){
            if(flag.size()>=1){
                flag.add(j);
                return true;
            }
            return f(list,target,j-1,flag);
        }
        if(list.get(j)>target){
            return f(list,target,j-1,flag);
        }
        flag.add(j);
        if(f(list,target-list.get(j),j-1,flag)){
            return true;
        }else{
            flag.remove(new Integer(j));
            return f(list,target,j-1,flag);
        }
    }
}


编辑于 2023-04-08 16:34:18 回复(2)
def dfs(idx,n):
    if(idx >= n):
        used[n] = True
        return
    #arr[idx]-1是去掉本身
    findSum(0,idx,0,ls[idx]-1,n)
def findSum(start,end,cnt,target,n):
    #从[start,end),未使用的节点节点中寻找是否有和为target的且个数cnt>=2的(因为子节点个数>=2)
    if target == 0:
        if cnt >= 2:
            # 当前节点找到符合条件的子节点,遍历下一个节点
            dfs(end+1,n)
    else:
        i=start
        while i < end and ls[i]<=target:
            if used[i]==True:
                i+=1
            # 剪枝,上一个同样值别人不要我也不要
            elif i>0 and ls[i]==ls[i-1] and used[i-1]==False:
                i+=1
            else:
                used[i] = True
                # 回溯,假装没使用这个数
                findSum(i+1,end,cnt+1,target-ls[i],n)
                used[i] = False
                i+=1
while True:
    try:
        n = int(input())
        ls = list(map(int,input().split()))
        ls.sort()
        used = [False] * (n+1)
        if 2 in ls&nbs***bsp;n not in ls:
            print('NO')
            continue
        for idx in range(n):
            if ls[idx]>1:
                break  #找到第一个不为1的数的下标
        dfs(idx,n)
        if used[n]==True:    print('YES')
        else:                print('NO')
    except:break

发表于 2022-03-28 15:14:20 回复(2)
wa7,不知道有哪组数据错了。维护儿子和直接下属的个数。
#include "bits/stdc++.h"
using namespace std;

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int n; while(cin >> n) {
        vector<int> a(n + 1);
        bool flag = 1;
        for(int i = 0;i < n; i++) {
            int x; cin >> x;
            a[x] ++;
            if(x == 0 || x == 2) flag = 0;
        }
        if(a[1] == n) flag = 0;
        int b = a[1];
        a[1] = 0;
        for(int i = 2;i <= n; i++) {
            while(a[i]) {
                if(b + a[1] < max(2, i - 1)) {
                    flag = 0;
                    break ;
                }
                int temp = max(1, i - a[1] - 1 - 1);
                a[1] += temp + 1;
                b -= temp;
                a[i]--;
            }
        }

        if(b != 1 || b + a[1] != n) flag = 0;
        for(int i = 2;i <= n; i++) if(a[i]) flag = 0;
        cout << (flag ? "YES" : "NO") << endl;
    }
}


发表于 2022-02-10 21:10:08 回复(0)
***** timu
lines = readLines("stdin")
l1 = strsplit(lines, " ")[[1]]
l2 = strsplit(lines, " ")[[2]]
l3 = strsplit(lines, " ")[[3]]
l4 = strsplit(lines, " ")[[4]]

l2 = lapply(l2, as.numeric)
l5 = {}
l6 = {}
for (i in 1:length(l2)){
    if (l2[i] != 1 & l2[i] == i){
        l5 = 1
    }
    else{
        l5 = 0
    }
}
for (i in 1:length(l4)){
    if (l4[i] != 1 & l4[i] == i){
        l6 = 1
    }
    else{
        l6 = 0
    }
}

if (l5 ==1){
    cat("YES")
    }else{
    cat("NO")
}
if (l6 ==1){
    cat("YES")
    }else{
    cat("NO")
}


发表于 2021-03-09 16:35:45 回复(1)