回溯算法(pta练习)

1 最佳调度问题

假设有n(n<=20)个任务由k(k<=20)个可并行工作的机器完成。完成任务i需要的时间为ti。 试设计一个算法,对任意给定的整数n和k,以及完成任务i 需要的时间为ti ,i=1~n。计算完成这n个任务的最佳调度,使得完成全部任务的时间最早。

输入格式:
输入数据的第一行有2 个正整数n和k。第2 行的n个正整数是完成n个任务需要的时间。

输出格式:
将计算出的完成全部任务的最早时间输出到屏幕。

输入样例:
在这里给出一组输入。例如:

7 3
2 14 4 16 6 5 3

输出样例:
在这里给出相应的输出。例如:

17
import java.util.*;
public class Main {
	static int n, k;
	static int[] machine;		//机器加入的任务用时
	static int[] task;			//每个任务完成需要的时间
	static int minTime;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		k = sc.nextInt();
		task = new int[n];
		machine = new int[k];
		for(int i=0;i<n;i++) {
			task[i] = sc.nextInt();
			minTime+=task[i];
		}
		recursion(0);
		System.out.println(minTime);
	}
	public static void recursion(int i) {
		if(i<n) {
			for(int j=0;j<k;j++) {
				//由第j台机器完成第i项任务
				machine[j]+=task[i];
				if(machine[j]<minTime) {	//判断是否已经大于了现有的最小时间
					recursion(i+1);
				}
				//第j机器不完成第i项任务
				machine[j]-=task[i];
			}
		}
		else {
			int nowMaxTime = 0;				//所有任务完成的最短时间取决于最后一个任务完成的机器的用时
			for(int j=0;j<k;j++) {
				nowMaxTime = Math.max(nowMaxTime, machine[j]);
			}
			minTime = Math.min(minTime, nowMaxTime);	//保证用时的最短
		}
	}
}

2 八皇后问题

在国际象棋中,皇后是最厉害的棋子,可以横走、直走,还可以斜走。棋手马克斯·贝瑟尔 1848 年提出著名的八皇后问题:即在 8 × 8 的棋盘上摆放八个皇后,使其不能互相攻击 —— 即任意两个皇后都不能处于同一行、同一列或同一条斜线上。例如:

题图.jpg

现在我们把棋盘扩展到 n×n 的棋盘上摆放 n 个皇后,请问该怎么摆?

请编写程序,输入正整数 n,输出全部摆法(棋盘格子空白处显示句点“.”,皇后处显示字母“Q”,每两个字符之间空一格)。

输入格式
正整数 n(n>0)

输出格式
若问题有解,则输出全部摆法(每两种摆法之间空一行)。
若问题无解,则输出 None。

要求:试探的顺序按从上到下逐行进行,其中每一行按从左到右的逐格进行,请参看输出样例2。

输入样例1

3

输出样例1

None

输入样例2

6

输出样例2

. Q . . . .
. . . Q . .
. . . . . Q
Q . . . . .
. . Q . . .
. . . . Q .

. . Q . . .
. . . . . Q
. Q . . . .
. . . . Q .
Q . . . . .
. . . Q . .

. . . Q . .
Q . . . . .
. . . . Q .
. Q . . . .
. . . . . Q
. . Q . . .

. . . . Q .
. . Q . . .
Q . . . . .
. . . . . Q
. . . Q . .
. Q . . . .
import java.util.*;
class Q {						//皇后类
	int x,y;
	public Q(int x, int y) {
		this.x = x;
		this.y = y;
	}
}
public class Main {
	static int n;
	static int[][] chressBoard;
	static ArrayList<Q> list = new ArrayList<Q>();		//用于存放每个皇后的位置
	static boolean Flag = true;
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		chressBoard = new int[n][n];
		recursion(0,1, chressBoard);
		if(Flag)
			System.out.println("None");
	}
	public static void recursion(int x, int count, int[][] chressBoard) {
		for(int y=0;y<n;y++) {
			if(chressBoard[x][y] == 0) {
				if(list.isEmpty()) {
					chressBoard[x][y] = 1;//标记皇后的位置为1。
					list.add(new Q(x,y));
				}
				else {
					boolean flag = true;
					for(int i=0,size=list.size();i<size;i++) {
						Q tmp = list.get(i);
						//判断任意两个皇后都不能处于同一行、同一列或同一条斜线上
						if(tmp.x==x || tmp.y==y || (Math.abs(tmp.x-x)==Math.abs(tmp.y-y)))
							flag = false;
					}
					if(flag) {
						chressBoard[x][y] = 1;			//标记皇后的位置为1。
						list.add(new Q(x,y));
					}
					else
						continue;
				}
				if(x<n-1) {
					recursion(x+1, count+1, chressBoard);
				}
				if(count == n) {		//存在解
					Flag = false;
					//将结果输出
					for(int i=0;i<n;i++) {
						for(int j=0;j<n;j++) {
							if(chressBoard[i][j]==1) {
								if(j<n-1)
									System.out.print("Q ");
								else 
									System.out.print("Q");
							}
							else {
								if(j<n-1)
									System.out.print(". ");
								else 
									System.out.print(".");
							}
						}
						System.out.println();
					}
					System.out.println();		
				}
				chressBoard[x][y] = 0;					//还原对皇后位置在图中的标记为0
				list.remove(list.size()-1);				//在list中删除对皇后的标记
			}
		}
	}
}

3 0-1背包

给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。

输入格式:
共有n+1行输入: 第一行为n值和c值,表示n件物品和背包容量c; 接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。

输出格式:
输出装入背包中物品的最大总价值。

输入样例:
在这里给出一组输入。例如:

5 10
2 6
2 3
6 5
5 4
4 6

输出样例:
在这里给出相应的输出。例如:

15
import java.util.Scanner;
public class Main {
static Scanner in=new Scanner(System.in); 
public static int ZeroOnePack2(int C,int N,int[] weight,int[] value){
	//动态规划
	int[] dp = new int[C+1];
	for(int i=1;i<N+1;i++){
		//逆序实现
		for(int j=C;j>=weight[i-1];j--){
			dp[j] = Math.max(dp[j-weight[i-1]]+value[i-1],dp[j]);
		}
	}
	return dp[C];		
}
public static  void main(String[] args) {
int n = in.nextInt();//n件物品
int c = in.nextInt();//背包容量
int[] weight = new int[n];
int[] value = new int [n];
for(int i=0;i<n;i++) {
  weight[i] = in.nextInt();
  value[i] = in.nextInt();
}
System.out.println(ZeroOnePack2(c,n,weight,value));
}
}

4 整数拆分

将一个正整数拆分成若干个正整数的和。

输入格式:
一个正整数n

输出格式:
若干行,每行一个等式(每个数或者等号间都有一个空格,第一个数前没有空格,最后一个数后面没有空格,数与数之间要求非降序排列)。最后一行给出解的总个数

输入样例:
在这里给出一组输入。例如:

4

输出样例:
在这里给出相应的输出。例如:

4 = 1 + 1 + 1 + 1
4 = 1 + 1 + 2
4 = 1 + 3
4 = 2 + 2
4
import java.util.*;
class Composition extends ArrayList<Integer> {
    @Override
    public boolean equals(Object other) {
        Composition comp = (Composition) other;
        Collections.sort(this);
        Collections.sort(comp);
        if (this.isEmpty() || comp.isEmpty() || this.size() != comp.size())
            return false;
        for (int i = 0; i < this.size(); i++)
            if (this.get(i) != comp.get(i))
                return false;
        return true;
    }

    @Override
    public int hashCode() {
        return 0;
    }
}
public class Main{
	static int n ;
	public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        System.out.print(toStr(calc(n)));
    }

    public static Set<Composition> calc(int n) {
        Set<Composition> possibility = new HashSet<Composition>();
        Composition composition = new Composition();
        switch (n) {
        case 1:
            composition.add(1);
            possibility.add(composition);
            return possibility;
        case 2:
            composition.add(1);
            composition.add(1);
            possibility.add(composition);
            return possibility;
        default:
            for (int i = 1; i <= n / 2; i++) {
                composition = new Composition();
                composition.add(i);
                composition.add(n - i);
                possibility.add(composition);
                if (i <= n - i) {
                    Set<Composition> partial_pos = calc(n - i);
                    for (Composition pos : partial_pos) {
                        pos.add(i);
                        possibility.add(pos);
                    }
                }
            }
            return possibility;
        }

    }

    public static String toStr(Set<Composition> possibility) {
        String str = new String();
        for (Composition pos : possibility)
            str += toStr(pos);
        str += possibility.size();
        return str;
    }


    public static String toStr(Composition composition) {
        String str = n+" = "+composition.get(0) + "";
        for (int i = 1; i < composition.size(); i++)
            str += (" + " + composition.get(i));
        str += "\n";
        return str;
    }
}

5 666

小明有一张m*n的好习惯记录卡,记录每一天的好习惯目标达成度(数字0-9表示)。某天目标完成达成,就在当天的格子里写上数字6,目标没有完全达成就写上一个小于6的数字(0-5),目标超额完成就写上一个大于6的数字(7-9)。记录卡上如果能找到一条长度为3的路径并且路径上的三个数字都大于等于6(这里的路径是指从某个格子出发,可以向左、右、上、下格子移动,并且不能重复经过一个格子),则小明就能得到一个“666”奖励。
请你帮小明统计下他总共能得到多少“666”奖励。

输入格式:
输入第一行给出两个正整数m,n(1=<m,n<=100),随后是m行,每行包含n个0-9之间的数字。

输出格式:
先输出m行,每行包括n个整数,代表从当前格子出发得到的“666”奖励个数,中间用空格分割,最后一个数字后面不带空格。然后再在下一行输出得到的“666”奖励总数。

输入样例:

3 3
6 6 7
3 8 3
7 9 5

输出样例:

2 1 2
0 3 0
1 1 0
10
#include <iostream>
using namespace std;
int n,m;
int a[1000][1000],b[1000][1000];
int backtrack(int x1,int x2,int x3,int x4)
{
 int x=0,y=0;
 if(a[x1][x2+1]>=6&&x4!=3)
 {
  if(x3==2)
   y++;
  else
   x+=backtrack(x1,x2+1,x3+1,1);
 }
 if(a[x1+1][x2]>=6&&x4!=4)
 {
  if(x3==2)
   y++;
  else
   x+=backtrack(x1+1,x2,x3+1,2);
 }
 if(a[x1][x2-1]>=6&&x4!=1)
 {
  if(x3==2)
   y++;
  else
   x+=backtrack(x1,x2-1,x3+1,3);
 }
 if(a[x1-1][x2]>=6&&x4!=2)
 {
  if(x3==2)
   y++;
  else
   x+=backtrack(x1-1,x2,x3+1,4);
 }
 if(x3==2)
  return y;
 else
  return x;
}
int main()
{
 int i,j;
 int sum=0;
 cin>>m>>n;
 for(i=1;i<=m;i++)
  for(j=1;j<=n;j++)
   cin>>a[i][j];
 for(i=1;i<=m;i++)
  for(j=1;j<=n;j++)
  {
   if(a[i][j]>=6)
    b[i][j]=backtrack(i,j,1,0);
   else
    b[i][j]=0;
  }
 for(i=1;i<=m;i++)
 {
  for(j=1;j<=n;j++)
  {
   if(j!=1)
    cout<<" ";
   cout<<b[i][j];
   sum+=b[i][j];
  }
  cout<<endl;
 }
 cout<<sum<<endl;
 return 0;
} 

6 工作分配问题

设有n件工作分配给n个人。将工作i分配给第j个人所需的费用为cij 。 设计一个算法,对于给定的工作费用,为每一个人都分配1 件不同的工作,并使总费用达到最小。

输入格式:
输入数据的第一行有1 个正整数n (1≤n≤20)。接下来的n行,每行n个数,表示工作费用。

输出格式:
将计算出的最小总费用输出到屏幕。

输入样例:
在这里给出一组输入。例如:

3
10 2 3
2 3 4
3 4 5

输出样例:
在这里给出相应的输出。例如:

9
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int a[100][100],sum,minn,i,j,n;
bool b[100];
 
void dfs(int dep)
{
	int r;
	for (r=1;r<=n;++r)//dep表示第几个人,r表示工作。。。其实倒过来理解也未尝不可。。
	  if (!b[r])
	  {
	  	b[r]=1;
	  	sum+=a[dep][r];//a[dep][r]表示第dep个人做第r个工作的费用
	  	if (dep==n)
	  	{
	  		if (sum<minn)
	  		  minn=sum;
	  	}
	  	else 
	  	 if (sum<minn)//最优化剪枝。。如果当前的费用已经大于或等于当前已知的最小费用,那就没有搜索下去的必要了
		  dfs(dep+1);
	  	sum-=a[dep][r];//回溯一步
	  	b[r]=0;
	  }
}
 
int main()
{
	scanf("%d",&n);
	for (i=1;i<=n;++i)
	  for (j=1;j<=n;++j)
	    scanf("%d",&a[i][j]);
	sum=0;
	minn=2147483647;//赋成最大值,,我愿意写这么一长串你管我。。。
	dfs(1);
	printf("%d",minn);
	return 0;
}
  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值