回溯法Q2——生命之树
生命之树
在X森林里,上帝创建了生命之树。
他给每棵树的每个节点(叶子也称为一个节点)上,都标了一个整数,
代表这个点的和谐值。上帝要在这棵树内选出一个非空节点集S,
使得对于S中的任意两个点a,b,都存在一个点列 {a, v1, v2, ..., vk, b}
使得这个点列中的每个点都是S里面的元素,且序列中相邻两个点间有一条边相连。
在这个前提下,上帝要使得S中的点所对应的整数的和尽量大。
这个最大的和就是上帝给生命之树的评分。
经过atm的努力,他已经知道了上帝给每棵树上每个节点上的整数。
但是由于 atm 不擅长计算,他不知道怎样有效的求评分。
他需要你为他写一个程序来计算一棵树的分数。
「输入格式」
第一行一个整数 n 表示这棵树有 n 个节点。
第二行 n 个整数,依次表示每个节点的评分。
接下来 n-1 行,每行 2 个整数 u, v,表示存在一条 u 到 v 的边。
由于这是一棵树,所以是不存在环的。
「输出格式」
输出一行一个数,表示上帝给这棵树的分数。
「样例输入」
5
1 -2 -3 4 5
4 2
3 1
1 2
2 5
「样例输出」
8
「数据范围」
对于 30% 的数据,n <= 10
对于 100% 的数据,0 < n <= 10^5, 每个节点的评分的绝对值不超过 10^6 。
资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗 < 3000ms
这是一颗无向赋权图,要求是找一棵最大的连通块(生成树)。
package _2015;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
* @author JohnnyLin
* @version Creation Time:2020年10月12日 下午11:04:40
*/
public class _t10生命之树 {
static int n;
static long ans;
//每个数组元素为列表
static List<Integer> nodeEdge[];
static long[] value;
static void dfs(int son,int father) {
for(int i=0;i<nodeEdge[son].size();i++) {
int next=nodeEdge[son].get(i);
if(next!=father) {//相邻结点不是父结点
//子树最大连通块为负值则不要了 比如说3号结点的子树为负值
dfs(next, son);
value[son]+=Math.max(0,value[next]);
//System.out.println(value[next]);
}
}
//根结点可能是负值
ans=Math.max(ans,value[son]);
//System.out.println(ans);
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
n=reader.nextInt();
value=new long[n+1];
nodeEdge=new ArrayList[n+1];
for(int i=1;i<=n;i++) {
value[i]=reader.nextInt();
nodeEdge[i]=new ArrayList<Integer>();
}
for(int i=1;i<n;i++) {
int u=reader.nextInt();
int v=reader.nextInt();
nodeEdge[u].add(v);
nodeEdge[v].add(u);
}
dfs(1,-1);
System.out.println(ans);
}
}
另一种写法:
参考了网上代码的
package 第六届;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner;
public class 生命树_网友版 {
static int[] nodeValue;//存放各个点的和谐值
static List<Integer>[] point;//存放各个点的包含的邻接边
static int[] value;//存放以某个点为根节点情况下的最大和谐值和
static int ans=0;//记录最大结果
static void dfs(int son,int father) {
value[son]=nodeValue[son];//以son为根节点,一开始时其最大和谐值为它本身
for(int i=0;i<point[son].size();i++) {//枚举与son相连的结点
int next=point[son].get(i);
//当枚举的下一个结点是son先前经过的点father 跳过 防止绕圈
if(next==father)continue;//如果都continue掉说明这个点是叶子结点
dfs(next,son);
//回溯(子节点平行状态都走完没得走了或者说状态没得转移了才会回溯)得到父节点与子节点和
if(value[next]>0)//子节点的和谐值
value[son]+=value[next];
ans=Math.max(ans, value[son]);
}
}
public static void main(String[] args) {
Scanner reader=new Scanner(System.in);
int n=reader.nextInt();
nodeValue=new int [n+1];
value=new int[n+1];
point=new LinkedList[n+1];
for(int i=1;i<=n;i++) {
nodeValue[i]=reader.nextInt();
point[i]=new LinkedList();
}
for(int i=1;i<n;i++) {
int x=reader.nextInt();
int y=reader.nextInt();
point[x].add(y);
point[y].add(x);
}
dfs(1,-1);//从0号结点开始搜索 假设它的父节点为-1可以为其他的无关量
System.err.println(ans);
}
}
注意:java里调用栈最多一万层,因此使用dfs最多可以通过n <= 10,30%的数据
回溯法Q3——抽签
/*题目描述
X星球要派出一个5人组成的观察团前往W星。
其中:
A国最多可以派出4人。
B国最多可以派出2人。
C国最多可以派出2人。
D国最多可以派出1人。
E国最多可以派出1人。
F国最多可以派出3人。
那么最终派往W星的观察团会有多少种国别的不同组合呢?
下面的程序解决了这个问题。
数组a[] 中既是每个国家可以派出的最多的名额。
程序执行结果为:
DEFFF
CEFFF
CDFFF
CDEFF
CCFFF
CCEFF
CCDFF
CCDEF
BEFFF
BDFFF
BDEFF
BCFFF
BCEFF
BCDFF
BCDEF
....
(以下省略,总共101行)
*/
package 排列组合类;
public class 抽签 {
//数组a[]表示每个国家可以派出的最多名额 0-a[k]
static int a[]={4,2,2,1,1,3};
static int ans;
static void dfs(int index,int cnt,String s) {
//当前选a[index]类 还有cnt个名额 当前已经选的人有s
System.out.println(index+" "+cnt+" "+s);
if(cnt<0) {return;}
if(index==6) {
if(cnt==0) {
ans++;
System.out.println(s);
}
return;
}
//{4,2,2,1,1,3}
//{AAAA,BB,CC,D,E,FFF}
String s2=s;
for(int i=0;i<=a[index];i++) {
dfs(index+1,cnt-i,s2);
s2=s2+(char)('A'+index);
}
}
public static void main(String[] args) {
dfs(0,5,"");
System.out.println(ans);
}
}