首页 > 试题广场 >

种树

[编程题]种树
  • 热度指数:7663 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 32M,其他语言64M
  • 算法知识视频讲解
小多想在美化一下自己的庄园。他的庄园毗邻一条小河,他希望在河边种一排树,共 M 棵。小多采购了 N 个品种的树,每个品种的数量是 Ai (树的总数量恰好为 M)。但是他希望任意两棵相邻的树不是同一品种的。小多请你帮忙设计一种满足要求的种树方案。

输入描述:
第一行包含一个正整数 N,表示树的品种数量。
第二行包含 N 个正整数,第 i (1 <= i <= N) 个数表示第 i 个品种的树的数量。
数据范围:
1 <= N <= 1000
1 <= M <= 2000


输出描述:
输出一行,包含 M 个正整数,分别表示第 i 棵树的品种编号 (品种编号从1到 N)。若存在多种可行方案,则输出字典序最小的方案。若不存在满足条件的方案,则输出"-"。
示例1

输入

3
4 2 1

输出

1 2 1 2 1 3 1
import java.util.*;

/**
 * 深度遍历,优先选择种类小的树
 **/

public class Main {
    private static boolean dfs(int[] curr, int index, int[] arr, int last) {
        int rest = curr.length - index;
        if (rest == 0) {
            for (int i : curr)    System.out.print(i + 1 + " ");
            return true;
        }
        
        for (int tree : arr) {
            if (tree > (rest + 1) / 2)    return false;
        }
        
        for (int i = 0; i < arr.length; i++) {
            if (arr[i] != 0 && i != last) {
                int[] temp = new int[arr.length];
                System.arraycopy(arr, 0, temp, 0, arr.length);
                temp[i]--;
                int[] currTemp = new int[curr.length];
                System.arraycopy(curr, 0, currTemp, 0, curr.length);
                currTemp[index] = i;
                if (dfs(currTemp, index + 1, temp, i))    return true;
            }
        }
        
        return false;
    }
    
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int count = scanner.nextInt();
        int[] arr = new int[count];
        int total = 0;
        for (int i = 0; i < count; i++) {
            int temp = scanner.nextInt();
            arr[i] = temp;
            total += temp;
        }
        scanner.close();
        
        int[] curr = new int[total];
        if (!dfs(curr, 0, arr, -1)) {
            System.out.println("-");
        }
    }
}

发表于 2020-04-09 19:03:59 回复(0)

思路:
使用搜索来做,但纯粹使用搜索的话通过率为90%,有一个点会超时,所以需要剪枝,一个简单的剪枝思路是比较当前未种的树和坑的大小关系!
具体的剪枝思路是每次搜索之前判断当前剩余的坑位left和任意品种的树之间的关系:

1) 如果left为偶数,那么只要tree[i] > left / 2,就表示肯定种不了
2) 如果left为奇数,那么只要tree[i] > (left + 1) / 2,就表示肯定种不了
这里有一个小技巧:left为偶数时,left/2 和(left + 1)/2的值是相等的,所以可以统一使用tree[i] > (left+1)/2的关系来做剪枝优化!

import java.util.*;

public class Main {

    static int n, m;
    static int[] tree;
    static List<String> ans;

    static boolean check(int left) {
        for (int i = 1; i <= n; i++) {
            if (tree[i] > (left + 1) / 2) return false;
        }
        return true;
    }

    static boolean dfs(int idx) {
        if (!check(m - idx)) return false;
        if (idx == m) {
            return true;
        } else {
            for (int i = 1; i <= n; i++) {
                if (idx == 0 || (tree[i] != 0 && i != Integer.valueOf(ans.get(idx - 1)))) {
                    tree[i]--;
                    ans.add(i + "");
                    if (dfs(idx + 1)) return true;
                    ans.remove(ans.size() - 1);
                    tree[i]++;
                }
            }
        }
        return false;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()) {
            n = sc.nextInt();
            tree = new int[n + 1];
            for (int i = 1; i <= n; i++) {
                tree[i] = sc.nextInt();
                m += tree[i];
            }
            ans = new ArrayList<>();
            if (dfs(0)) {
                System.out.println(String.join(" ", ans));
            } else {
                System.out.println("-");
            }
        }
    }
}
编辑于 2019-07-22 10:14:58 回复(4)

importjava.util.Scanner;

//1、首先判断能不能种树。如果是奇数,如果某种树大于(M+1)的一半则不能种,

如果是偶数,则大于M的一半不能种树

// 2、每次种树前都要判断是不是某种树大于一半,如果是,本次种该树。

如果不是再从下网上遍历数组,优先种序号小的树(要保证该序号的树还有的剩,且不等于之前种的树)

publicc***in {

publicstaticvoidmain(String[] args) {

Scanner input =newScanner(System.in);

intN=input.nextInt();

int[] nums=newint[N];

intM=0;

for(inti = 0; i <N ; i++) {

nums[i]=input.nextInt();

M+=nums[i];

}

if(M%2==0){//如果是偶数

for(inti = 0; i <N ; i++) {

if(nums[i]>M/2){

System.out.println("-");

return;

}

}

}else{//如果是奇数

for(inti = 0; i <N ; i++) {

if(nums[i]>(M+1)/2){

System.out.println("-");

return;

}

}

}

StringBuffer res=newStringBuffer();

intcount=M;

intpre=-1;

for(inti=0;i<M;i++){

booleanflag=false;

for(intk=0;k<N;k++){

if(nums[k]>count/2){//判断是否有树大于剩余的一半

res.append(k+1);

res.append(" ");

nums[k]--;

pre=k+1;

flag=true;

count--;

break;

}

}

if(flag==true) continue;

intj=0;

while(nums[j]==0||(j+1)==pre){

j++;

}

nums[j]--;

pre=j+1;

res.append(j+1);

res.append(" ");

count--;

}

System.out.println(res.toString().trim());

}

}

编辑于 2019-05-14 21:34:27 回复(1)