小多想在美化一下自己的庄园。他的庄园毗邻一条小河,他希望在河边种一排树,共 M 棵。小多采购了 N 个品种的树,每个品种的数量是 Ai (树的总数量恰好为 M)。但是他希望任意两棵相邻的树不是同一品种的。小多请你帮忙设计一种满足要求的种树方案。
小多想在美化一下自己的庄园。他的庄园毗邻一条小河,他希望在河边种一排树,共 M 棵。小多采购了 N 个品种的树,每个品种的数量是 Ai (树的总数量恰好为 M)。但是他希望任意两棵相邻的树不是同一品种的。小多请你帮忙设计一种满足要求的种树方案。
第一行包含一个正整数 N,表示树的品种数量。
第二行包含 N 个正整数,第 i (1 <= i <= N) 个数表示第 i 个品种的树的数量。
数据范围:
1 <= N <= 1000
1 <= M <= 2000
输出一行,包含 M 个正整数,分别表示第 i 棵树的品种编号 (品种编号从1到 N)。若存在多种可行方案,则输出字典序最小的方案。若不存在满足条件的方案,则输出"-"。
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("-"); } } }
思路:
使用搜索来做,但纯粹使用搜索的话通过率为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("-"); } } } }
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());
}
}