2.2最优装载问题
import java.util.Scanner;
import java.util.Arrays;
public class Test2_2 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入载重量c及古董个数n:");
int c=scanner.nextInt(); //载重量
int n=scanner.nextInt(); //古董个数
int[] weight=new int[n];
System.out.println("请输入每个古董的重量:");
for(int i=0;i<n;i++)
weight[i]=scanner.nextInt();
Arrays.sort(weight);
int temp=0; //记录装载到船上的古董重量
int answer=0; //记录已装载的古董个数
for(int i=0;i<n;i++) {
temp+=weight[i]; //贪心策略:每次装入最轻者
if(temp<=c) //若加入最轻者后还小于载重量,则古董个数+1
answer++;
else
break;
}
System.out.println("能装入的古董最大数量为answer="+answer);
scanner.close();
}
}
程序运行结果如下:
2.3背包问题
/*阿里巴巴与四十大盗——背包问题*/
import java.util.Scanner;
class three {
double w=0; //每个宝物的重量
double v=0; //每个宝物的价值
double p=0; //性价比
}
public class Test2_3 {
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入宝物数量n及毛驴的承载能力m:");
int n=scanner.nextInt(); //表示n个宝物
double m=scanner.nextDouble(); //表示毛驴的承载能力
System.out.println("请输入每个宝物的重量和价值,用空格分开:");
three[] s=new three[n]; //定义n长的数组存放宝物,数组类型是three型,即数组每位由重量、价格和性价比组成
for(int i=0;i<n;i++) {
s[i]=new three();
s[i].w=scanner.nextDouble();
s[i].v=scanner.nextDouble();
s[i].p=s[i].v/s[i].w;
}
selectionSort(s);
double sum=0; //表示贪心记录运走宝物的价值之和
for(int i=0;i<n;i++) {
if(m>s[i].w) { //如果宝物的重量小于毛驴剩下的承载能力
sum+=s[i].v; //宝物价值之和增加
m-=s[i].w; //毛驴承载能力减少
}
else { //如果宝物的重量大于毛驴剩下的承载能力,则进行部分装载
sum+=m*s[i].p; //价值增加部分重量的价值
break;
}
}
System.out.println("装入宝物的最大价值Maximum value="+sum);
scanner.close();
}
public static void selectionSort(three[] array){ //运用选择排序将宝物按照性价比由大到小进行排序
three temp=new three();
for(int i=0;i<array.length-1;i++){
for(int j=i+1;j<=array.length-1;j++){
if(array[i].p<array[j].p){
temp = array[i]; //要将类的所有成员变量进行换位置,因为值是相互对应的
array[i] = array[j];
array[j] = temp;
}
}
}
}
}
程序运行结果如下:
2.4会议安排
import java.util.Scanner;
class Meet{
int beg; //会议的开始时间
int end; //会议的结束时间
int num; //记录会议的编号
}
class SetMeet{
private int n; //会议总数
private int answer; //最大的安排会议总数
private int last; //记录被选中会议的结束时间
Meet[] meet=new Meet[1000]; //存放会议对象的数组
/*读入数据*/
public void init() {
Scanner scanner=new Scanner(System.in);
System.out.println("输入会议总数:");
n=scanner.nextInt();
System.out.println("输入会议的开始时间和结束时间,以空格分开:");
for(int i=0;i<n;i++) {
meet[i]=new Meet(); //初始化对象
meet[i].beg=scanner.nextInt();
meet[i].end=scanner.nextInt();
meet[i].num=i+1;
}
scanner.close();
}
public void solve() {
selectionSort(meet); //对会议按照结束时间由小到大排序
System.out.println("排完序的会议时间如下:");
System.out.println("会议编号 开始时间 结束时间");
for(int i=0;i<n;i++)
System.out.println(" "+meet[i].num+" "+meet[i].beg+" "+meet[i].end);
System.out.println();
System.out.println("选择会议的过程:");
System.out.println(" 选择第"+meet[0].num+"个会议"); //选中了第一个会议
answer=1;
last=meet[0].end;
for(int i=1;i<n;i++) { //依次检查每个会议
if(meet[i].beg>=last) { //如果会议i开始的时间大于等于最后一个选中的会议的结束时间
System.out.println(" 选择第"+meet[i].num+"个会议");
answer++; //可安排的会议总数+1
last=meet[i].end; //更新last为最后一个选中会议的结束时间
}
}
System.out.println("最多可以安排"+answer+"个会议");
}
public void selectionSort(Meet[] array){ //运用选择排序对会议按结束时间由小到大排序
Meet temp=new Meet();
for(int i=0;i<n-1;i++){
for(int j=i+1;j<=n-1;j++){
if(array[i].end>array[j].end){
temp = array[i]; //要将类的所有成员变量进行换位置,因为值是相互对应的
array[i] = array[j];
array[j] = temp;
}
}
}
}
}
public class Test2_4 {
public static void main(String[] args) {
SetMeet sm=new SetMeet(); //创建对象
sm.init(); //读入数据
sm.solve(); //贪心算法求解
}
}
程序运行结果如下:
2.6 哈夫曼编码
import java.util.Scanner;
/*结点类*/
class HNodeType{
double weight; //权值
int parent; //父亲结点
int lchild; //左孩子
int rchild; //右孩子
char value; //该节点表示的字符
}
/*编码类*/
class HCodeType{
final int MAXBIT=100;
int[] bit=new int[MAXBIT]; //存储编码的数组
int start; //编码开始下标
}
public class Test2_6 {
static final int MAXVALUE=10000;
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("请输入结点个数:");
int n=scanner.nextInt();
HCodeType[] HuffCode=new HCodeType[n]; //定义一个编码类数组
HNodeType[] HuffNode=new HNodeType[2*n-1]; //定义一个结点类数组
HuffmanTree(HuffNode,n); //构造哈夫曼树
HuffmanCode(HuffNode,HuffCode,n); //哈夫曼树编码
//输出已保存好的哈夫曼编码
for(int i=0;i<n;i++) {
System.out.print(HuffNode[i].value+": Huffman code is: ");
for(int j=HuffCode[i].start+1;j<n;j++)
System.out.print(HuffCode[i].bit[j]);
System.out.println();
}
scanner.close();
}
/*构造哈夫曼树*/
public static void HuffmanTree(HNodeType[] HuffNode,int n) {
Scanner scanner=new Scanner(System.in);
int i,j,x1,x2;
double m1,m2;
for(i=0;i<2*n-1;i++) { //初始化存放哈夫曼树数组中的结点
HuffNode[i]=new HNodeType();
HuffNode[i].weight=0;
HuffNode[i].parent=-1;
HuffNode[i].lchild=-1;
HuffNode[i].rchild=-1;
}
for(i=0;i<n;i++) { //输入n个叶子结点的权值
System.out.println("请输入第"+(i+1)+"个结点的字符和权值:");
String str=scanner.next();
HuffNode[i].value=str.charAt(0);
HuffNode[i].weight=scanner.nextDouble();
}
for(i=0;i<n-1;i++) { //执行n-1次合并
m1=m2=MAXVALUE; //存放无父节点且权值最小的两个结点的权值
x1=x2=-1; //权值最小的两个结点在数组中的下标
for(j=0;j<n+i;j++) { //找出无父节点且权值最小的两个结点,合并成一颗二叉树
if(HuffNode[j].weight<m1&&HuffNode[j].parent==-1) {
m2=m1;
x2=x1;
m1=HuffNode[j].weight;
x1=j;
}
else if(HuffNode[j].weight<m2&&HuffNode[j].parent==-1) {
m2=HuffNode[j].weight;
x2=j;
}
}
//设置找到的两个结点的父节点的信息
HuffNode[x1].parent=n+i;
HuffNode[x2].parent=n+i;
HuffNode[n+i].lchild=x1; //新结点的左孩子节点是权值最小的结点的下标
HuffNode[n+i].rchild=x2; //新结点的右孩子节点是权值次小的结点的下标
HuffNode[n+i].weight=m1+m2; //权值是两个最小权值之和
System.out.printf("x1和x2的权值分别是:%.2f %.2f",m1,m2);
System.out.println();
}
}
/*哈夫曼树编码*/
public static void HuffmanCode(HNodeType[] HuffNode,HCodeType[] HuffCode,int n) {
HCodeType cd=new HCodeType(); //定义临时变量存放求解编码时的信息
int c; //表示结点下标变量
int p; //表示当前结点的父节点
for(int i=0;i<n;i++) { //共有n个叶子结点
HuffCode[i]=new HCodeType();
cd.start=n-1; //从后往前存放
c=i;
p=HuffNode[c].parent;
while(p!=-1) { //自底向上遍历,只要存在父节点就继续向上走
if(HuffNode[p].lchild==c) { //若当前结点是p的左孩子则编码0
cd.bit[cd.start]=0;
}
else { //右孩子则编码1
cd.bit[cd.start]=1;
}
cd.start--; //求编码的下一位
c=p; //变量上移,准备下一次循环
p=HuffNode[c].parent;
}
//把叶子结点的编码信息从临时编码cd中复制到编码数组中
for(int j=cd.start+1;j<n;j++)
HuffCode[i].bit[j]=cd.bit[j];
HuffCode[i].start=cd.start; //指向编码开始的前一位
}
}
}
程序运行结果如下:
2.7最小生成树
import java.util.Scanner;
public class Test2_7 {
static final int INF=0x3fffffff;
static final int N=100;
static boolean[] s=new boolean[N]; //s[i]=true,说明顶点i已加入集合U中
static int[] closest=new int[N]; //表示集合V-U中顶点j到U集合的最邻近顶点
static int[] lowcost=new int[N]; //表示集合V-U中顶点j到U集合的最邻近距离
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("输入结点数n和边数m:");
int n=scanner.nextInt();
int m=scanner.nextInt();
int[][] c=new int[n+1][n+1]; //存放图的权值
for(int i=1;i<=n;i++) { //初始化邻接矩阵
for(int j=1;j<=n;j++)
c[i][j]=INF;
}
System.out.println("输入结点数u、v和边值w:");
int u,v,w;
for(int i=1;i<=m;i++) {
u=scanner.nextInt();
v=scanner.nextInt();
w=scanner.nextInt();
c[u][v]=c[v][u]=w;
}
System.out.println("输入任一结点u0:");
int u0=scanner.nextInt();
Prim(n,u0,c); //求最小权值之和
int sumcost=0;
System.out.println("数组lowcost的内容为:");
for(int i=1;i<=n;i++) {
System.out.print(lowcost[i]+" ");
sumcost+=lowcost[i];
}
System.out.println("最小的花费是:"+sumcost);
scanner.close();
}
public static void Prim(int n,int u0,int[][] c) {
s[u0]=true; //初始时集合U中只有一个元素即u0
for(int i=1;i<=n;i++) { //初始化closest、lowcost和s数组
if(i!=u0) {
lowcost[i]=c[u0][i];
closest[i]=u0;
s[i]=false; //除u0外其他顶点都不在集合U中
}
else
lowcost[i]=0;
}
for(int i=1;i<=n;i++) {
int temp=INF;
int t=u0;
for(int j=1;j<=n;j++) { //在集合V-U中寻找距离集合U最近的顶点t
if(!s[j]&&lowcost[j]<temp) { //!s[j]说明j顶点在V-U中
t=j;
temp=lowcost[j];
}
}
s[t]=true; //将t加入集合U中
for(int j=1;j<=n;j++) { //更新lowcost和closest
if(!s[j]&&c[t][j]<lowcost[j]) { //与t相邻的顶点到U的距离小于原来到U的距离则更新
lowcost[j]=c[t][j];
closest[j]=t;
}
}
}
}
}
程序运行结果如下:
Kruskal算法实现:
import java.util.Scanner;
class Edge{
int u; //边的两端点
int v;
int w; //权值
}
public class Test2_7_2 {
static final int N=100;
static int[] nodeset=new int[N]; //存放结点的集合号
public static void main(String[] args) {
Scanner scanner=new Scanner(System.in);
System.out.println("输入结点数n和边数m:");
int n=scanner.nextInt();
int m=scanner.nextInt();
Edge[] e=new Edge[m]; //存放m条边
Init(n); //初始化
System.out.println("输入结点数u、v和边值w:");
for(int i=0;i<m;i++) {
e[i]=new Edge();
e[i].u=scanner.nextInt();
e[i].v=scanner.nextInt();
e[i].w=scanner.nextInt();
}
selectionSort(e); //将边按权值从小到大排序
int ans=Kruskal(n,m,e);
System.out.println("最小的花费是:"+ans);
}
public static void Init(int n) {
for(int i=1;i<=n;i++)
nodeset[i]=i; //每个结点对应一个集合
}
public static void selectionSort(Edge[] e) { //选择排序对边权值从小到大排序
Edge temp=new Edge();
for(int i=0;i<e.length-1;i++) {
for(int j=i+1;j<=e.length-1;j++) {
if(e[i].w>e[j].w) {
temp=e[i];
e[i]=e[j];
e[j]=temp;
}
}
}
}
public static int Kruskal(int n,int m,Edge[] e) {
int ans=0,j=0; //用来控制已加入的边数
for(int i=0;i<m;i++) {
if(merge(e[i].u,e[i].v,n)) { //判断边的两端是否属于同一集合即是否构成回路
ans+=e[i].w; //若不构成回路则将改变加入
if(++j==(n-1)) //加入n-1条边即可
return ans;
}
}
return 0;
}
public static boolean merge(int u,int v,int n) { //合并集合
int p=nodeset[u]; //p为u结点的集合号
int q=nodeset[v]; //去为v结点的集合号
if(p==q) //说明u和v在一个集合中
return false;
for(int i=1;i<=n;i++) {
if(nodeset[i]==q)
nodeset[i]=p; //将和v相同的集合中的结点改为u的集合号
}
return true;
}
}