首页 > 试题广场 >

订单分配

[编程题]订单分配
  • 热度指数:1987 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 256M,其他语言512M
  • 算法知识视频讲解

打车派单场景, 假定有N个订单, 待分配给N个司机。每个订单在匹配司机前,会对候选司机进行打分,打分的结果保存在N*N的矩阵A 其中Aij 代表订单i司机j匹配的分值。

假定每个订单只能派给一位司机,司机只能分配到一个订单。求最终的派单结果,使得匹配的订单和司机的分值累加起来最大,并且所有订单得到分配。


输入描述:

第一行包含一个整数N,2≤N≤10。

第二行至第N+1行包含N*N的矩阵。



输出描述:
输出分值累加结果和匹配列表,结果四舍五入保留小数点后两位
(注意如果有多组派单方式得到的结果相同,则有限为编号小的司机分配编号小的订单,比如:司机1得到1号单,司机2得到2号单,就比司机1得到2号单,司机2得到1号单要好)
示例1

输入

3
1.08 1.25 1.5
1.5 1.35  1.75
1.22 1.48 2.5

输出

5.25
1 2
2 1
3 3

说明

第一行代表得到的最大分值累加结果5.25,四舍五入保留两位小数;

第二行至第四行代表匹配的结果[i j],其中i按行递增:

订单1被派给司机2,订单2被派给司机1,订单3被派给司机3。使得A12+ A21+ A33= 1.25 + 1.5 + 2.5 = 5.25在所有的组合中最大。

为什么会报错数组溢出或者递归层次太多 ,求帮忙看看
#include<bits/stdc++.h>
using namespace std;
vector<int>res;
vector<bool>tag;
double rmax=0;
void solve(vector<vector<double>>&A,vector<int>&v,double maxnum,int j){
    if(j>=A[0].size()){
        if(rmax<maxnum){
            rmax=maxnum;
            res=v;
        }
        return;
    }
    for(int i=0;i<A[0].size();i++){
        if(!tag[i]){
            tag[i]=true;
            v.push_back(i);
            solve(A,v,maxnum+A[j][i],j+1);
            v.pop_back();
            tag[i]=false;
        }
    }
}
int main(){
    int n;
    double a;
    cin>>n;
    vector<vector<double> >A;
    for(int i=0;i<n;i++){
        vector<double>temp;
        for(int j=0;j<n;j++){
            cin>>a;
            temp.push_back(a);
        }
        A.push_back(temp);
    }
    vector<int>v;
    tag=vector<bool>(n,false);
    solve(A,v,0,0);
    cout<<fixed<<setprecision(2)<<rmax<<endl;
    for(int i=0;i<n;i++)cout<<i+1<<" "<<res[i]+1<<endl;
    return 0;
}


发表于 2020-03-25 16:38:10 回复(0)
更多回答
import java.util.*;
//过100%
public class Main {
    private double t = 0.00;
    private int[] flag;
    private double res = -1000.00;//0.00只能过80%
    private ArrayList<Integer> add;
    public void Solution(double[][] nums)
    {
        int n = nums.length;
        flag = new int[n];
        for(int i = 0;i<n;i++)
        {
            ArrayList<Integer> temp = new ArrayList<>();
            temp.add(i+1);
            flag[i] = 1;
            t = nums[0][i];
            Best(temp, nums, 1);
            t = 0.00;
            flag[i] = 0;
        }
        System.out.println(String.format("%.2f", res));
        for(int i = 0;i<add.size();i++)
        {
            System.out.println(i+1 +" "+ add.get(i));
        }
    }
    public void Best(ArrayList<Integer> temp, double[][] nums, int end)
    {
        if(end==nums.length)
        {
            if(res<t)
            {
                res = t;
                add = (ArrayList<Integer>) temp.clone();
            }
        }
        for(int i = 0;i<nums.length;i++)
        {
            if(flag[i]!=1)
            {
                flag[i] = 1;
                t += nums[end][i];
                temp.add(i+1);
                Best(temp, nums, end+1);
                temp.remove(temp.size()-1);
                t -= nums[end][i];
                flag[i] = 0;
            }
        }
    }
    public static void main(String[] args)
    {
        Scanner in = new Scanner(System.in);
        while(in.hasNext())
        {
            int n = in.nextInt();
            double[][] nums = new double[n][n];
            for(int i = 0;i<n;i++)
                for(int j = 0;j<n;j++)
                    nums[i][j] = in.nextDouble();
            Main s = new Main();
            s.Solution(nums);
        }
    }
}
参考楼上的答案,修改了初始值后,过100%
发表于 2020-04-08 17:30:51 回复(0)
import java.util.*;
public class Main {
	private double t = 0.00;
	private int[] flag;
	private double res = 0.00;
	private ArrayList<Integer> add;
	public void Solution(double[][] nums)
	{
		int n = nums.length;
		flag = new int[n];	
		for(int i = 0;i<n;i++)
		{
			ArrayList<Integer> temp = new ArrayList<>();
			temp.add(i+1);
			flag[i] = 1;
			t += nums[0][i];
			Best(temp, nums, 1);
			t = 0.00;
			flag[i] = 0;
		}
		System.out.println(String.format("%.2f", res));
		for(int i = 0;i<add.size();i++)
		{
			System.out.println(i+1 +" "+ add.get(i));
		}
	}
	public void Best(ArrayList<Integer> temp, double[][] nums, int end)
	{
		if(end==nums.length)
		{
			if(res<t)
			{
				res = t;
				add = (ArrayList<Integer>) temp.clone();
			}
		}
		for(int i = 0;i<nums.length;i++)
		{
			if(flag[i]!=1)
			{
				flag[i] = 1;
				t += nums[end][i];
				temp.add(i+1);
				Best(temp, nums, end+1);
				temp.remove(temp.size()-1);
				t -= nums[end][i];
				flag[i] = 0;
			}
		}
	}
	public static void main(String[] args)
	{
		Scanner in = new Scanner(System.in);
		while(in.hasNext())
		{
			int n = in.nextInt();
			double[][] nums = new double[n][n];
			for(int i = 0;i<n;i++)
				for(int j = 0;j<n;j++)
					nums[i][j] = in.nextDouble();
			Main s = new Main();
			s.Solution(nums);
		}
	}
}
不是知道为什么越界 求大佬看看
发表于 2020-03-11 18:54:48 回复(6)
状压dp+记录路径,时间复杂度n^2*2^n,不过其实可以把状态预处理一下,可以做到n*(2^n)
#include<bits/stdc++.h>
using namespace std;
double dp[15][1<<12];    //表示到第i个司机时时1~i的司机的选择情况,用二进制表示,当前位为1,则代表被选了
  
double a[15][15];
const double inf=1e9;
int calc(int x){    //计算x二进制有多少个1
    int t=0;
    while(x){
        t+=x&1;
        x>>=1;
    }
    return t;
}
int fa[15][1<<12];    //用来记录当前状态由哪个状态转移过来,方便之后的路径查找
typedef pair<int,int>pii;
vector<pii>V;
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
        scanf("%lf",&a[i][j]);
    }
    dp[0][0]=0;
    int sz=1<<n;    
    for(int i=1;i<=n;i++){
        for(int j=0;j<sz;j++){
            dp[i][j]=-inf;
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<sz;j++){
            if(calc(j)!=i){    //因为到第i个司机只能是有i个订单被选了
                continue;
            }
            //cout<<i<<" "<<j<<endl;
            for(int k=1;k<=n;k++){
                        //当前枚举的状态中第k个订单没被选,则跳过
                if((j&(1<<(k-1)))==0)continue;
                        //当前枚举的状态第k个订单被选了,则由1~(i-1)的没选k的状态转移过来,因此异或掉
                int tmp=j^(1<<(k-1));
                if(dp[i][j]<dp[i-1][tmp]+a[n-i+1][k]){    //只有在更优的时候选择,保证一定先选订单号小的
                    dp[i][j]=dp[i-1][tmp]+a[n-i+1][k];
                    //cout<<i<<" "<<j<<" "<<k<<endl;
                    fa[i][j]=tmp;
                }
            }
        }
    }
      //fj代表最终状态 即111111...
    int fj=sz-1;
      //这里就在迭代求路径,由转移的记录来找,注意的是一直是把最后一个司机当第一个司机的
    for(int i=n;i>=1;i--){
        int tmp=fa[i][fj];
        for(int j=1;j<=n;j++){
                  //tmp^fj就能计算出当前这个司机选的是哪个订单(例如00010000)之类的,然后找一下是不是j这个订单
            if((1<<(j-1))&(tmp^fj)){
                V.push_back({n-i+1,j});
                break;
            }
        }
        fj=tmp;
    }
    printf("%.2f\n",dp[n][sz-1]);
    sort(V.begin(),V.end());
    for(auto var:V){
        printf("%d %d\n",var.first,var.second);
    }
    return 0;
}

编辑于 2020-04-16 15:14:54 回复(3)
//每行每列选一个,且不重复,类似于N皇后问题与全排列问题,回溯法解决
#include <iostream>
#include <vector>
#include <stdio.h>
using namespace std;

void recur(vector<vector<double>>& nums, vector<int>& ans, vector<int>& curT, vector<bool>& visited, double curProfit, double& sum, int row){
    int n = nums.size();
    //退出条件
    if(row == n){
        if(curProfit > sum){        //更新最大和与每行对应的列
            sum = curProfit;
            ans = curT;
        }
        return;
    }
    //轮流考虑当前row行,选取每一列的可能性
    for(int i=0; i<n; i++){
        //该列未选取
        if(!visited[i]){
            //更新状态
            visited[i] = true;
            curProfit += nums[row][i];
            curT[row] = i;
            
            recur(nums, ans, curT, visited, curProfit, sum, row+1);
            //还原状态
            visited[i] = false;
            curProfit -= nums[row][i];
        }
    }
}

int main(){
    int n;
    while(cin >> n){
        vector<vector<double>> nums(n, vector<double>(n));
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++)
                cin >> nums[i][j];
        }
        //最大和的情况下,各行选取的列; 当前状态下各行选取的列
        vector<int> ans(n), curT(n);
        //记录已选取的列,避免重复
        vector<bool> visited(n, false);
        //最大和与当前和
        double sum = -1, curProfit = 0;
        //初始行号
        int row = 0;
        recur(nums, ans, curT, visited, curProfit, sum, 0);
        
        printf("%4.2f\n", sum);
        
        for(int i=0; i<n; i++){
            cout << i+1 << ' ' << ans[i]+1 << endl;
        }
    }
    return 0;
}

回溯法解决
发表于 2020-07-28 21:15:18 回复(0)
import java.io.*;
/**
 * @Description: 计算并保存所有分组方式下的 score 值,取最大值 优化方向 -> 循环计算并 始终存储一组最好的值
 * @Param:
 * @return:
 * @Author: xulifeng
 * @Date: 3/10/2022
 */
public class Main {
    // 原始分配表
    static double[][] score;
    // 最终分配结果
    static double[][] bestPath;
    // 当前分配方式
    static double[][] curPath;
    // 最大score
    static double maxScore = 0;
    // 订单数
    static int orderNum;

    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        orderNum = Integer.parseInt(br.readLine());
        score = new double[orderNum][orderNum];
        bestPath = new double[orderNum][orderNum];
        curPath = new double[orderNum][orderNum];
        // 填充score、bestPath、curPath
        for (int i = 0; i < orderNum; i++) {
            String[] oneRowScore = br.readLine().split(" ");
            for (int j = 0; j < orderNum; j++) {
                score[i][j] = Double.parseDouble(oneRowScore[j]);
                bestPath[i][j] = 0;
                curPath[i][j] = 0;
            }
        }
        //从第0行开始递归
        backTracing(0, orderNum);
        System.out.printf("%.2f",maxScore);
        System.out.println();

        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                if (bestPath[i][j] != 0) {
                    // +1是因为订单 车是从1开始 而数组从零开始
                    System.out.print(i+1);
                    System.out.print(" ");
                    System.out.println(j+1);
                }
            }
        }
    }
    /**
     * @Description: 回溯算法 检查这个位置是否还能存放
     * @Param: 当前行curRow 总列数
     * @return:
     * @Author: xulifeng
     * @Date: 3/9/2022
     */
    public static void backTracing(int curRow, int totalCol) {
        // 遍历完所有行时 row 和 col 大小一样
        if (curRow == orderNum) {
            // 将深拷贝的数据添加进result 浅拷贝会因为回溯全部变为0
            double curScore = computeCurScore();
            if (curScore > maxScore) {
                maxScore = curScore;
                bestPath = deepCopy();
            }
            return;
        }
        //index 其实也为行 从上往下判断
        for (int index = 0; index < totalCol; index++) {
            if (isValid(curRow, index)) {
                // 可以分配订单
                curPath[curRow][index] = score[curRow][index];
                backTracing(curRow+1,totalCol);
                //撤销当前的操作 因为撤销操作 所有的bestPath数组最终都变成了0回溯不可避免的事情
                curPath[curRow][index] = 0;
            }
        }
    }
    // 检查目前是否能在 row col这个位置存放数据 要求同一列只能有一个数据
    public static boolean isValid(int curRow, int curCol){
        //检查[curRow][curCol]这一列之前是否已经有数据
        for (int i = 0; i < curRow; i++) {
            if (curPath[i][curCol] != 0) {
                return false;
            }
        }
        return true;
    }
    // 计算当前分配方式下的score
    public static double computeCurScore() {
        double score = 0;
        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                score += curPath[i][j];
            }
        }
        return score;
    }
    // 深拷贝
    public static double[][] deepCopy() {
        double[][] temp = new double[orderNum][orderNum];
        for (int i = 0; i < orderNum; i++) {
            for (int j = 0; j < orderNum; j++) {
                temp[i][j] = curPath[i][j];
            }
        }
        return temp;
    }
}
发表于 2022-03-10 11:29:44 回复(1)
通过排列组合查询出1-n所有的组合找出最小的组合
#include<bits/stdc++.h>
using namespace std;
double s[10][10];
int num[10];
int numt[10];
int n;
double ma;
int main(){
	// 输入参数 
	cin>>n;
	for(int i=0;i<n;i++){
		for(int j=0;j<n;j++){
			cin>>s[i][j];
		}
	}
	for(int i=0;i<n;i++){
		num[i]=i;
		numt[i]=i;
	} 
	// 查询最大值 
	ma=-100000;
	do{
		double sum=0;
		for(int i=0;i<n;i++){
			sum+=s[i][num[i]];
		}
		if(sum>ma){
			for(int i=0;i<n;i++){
				numt[i] = num[i];
			}
			ma=sum;
		}
	}while(next_permutation(num,num+n));  // 找到 0 - N 所有的排列组合 找出最大的一个 
	
	printf("%.2lf\n",ma);
	for(int i=0;i<n;i++){
		cout<<i+1<<" "<<numt[i]+1<<endl;
	}
	return 0;
}



发表于 2021-11-08 19:55:05 回复(1)
public class Main {

    public static List<Integer> best = new ArrayList<>();

    public static void main(String[] args) {
        double[][] matrix = {{1.08, 1.25, 1.5},
                {1.5, 1.35, 1.75},
                {1.22, 1.48, 2.5}};

        double grade = allPl(matrix, 0, new ArrayList<>(), 0);
        System.out.println(grade);
        best.stream().forEach(item -> System.out.println(item + 1));
    }

    public static double allPl(double[][] matrix, double maxNum, List<Integer> res, double num) {
        if (res.size() == matrix.length && num > maxNum) {
            maxNum = num;
            best = new ArrayList<>(res);
            return maxNum;
        }
        for (int i = 0; i < matrix.length; i++) {
            if (res.contains(i)) {
                continue;
            }
            res.add(i);
            num += matrix[res.size() - 1][i];
            maxNum = allPl(matrix, maxNum, res, num);
            res.remove(res.size() - 1);
            num -= matrix[res.size()][i];
        }
        return maxNum;
    }
}

发表于 2021-02-24 21:58:15 回复(0)
//回溯
#include <bits/stdc++.h> //我的不行
using namespace std;
double MAX=-1;
vector<int>vis(11,0);//司机是否有订单
double a[11][11];
void dfs(int n,int start,double sum,vector<pair<int,int> >&temp,vector<pair<int,int> >&res){
    if(start==n)
    {
        if(sum>MAX)
        {
            res.clear();
            MAX=sum;
            for(int i=0;i<temp.size();i++)
            {
                res.push_back(temp[i]);
            }
          
        }
    }else{
        for(int i=1;i<=n;i++){
            if(vis[i]==0)
            {
                vis[i]=1;
                temp.push_back(make_pair(start+1,i));
                dfs(n,start+1,sum+a[start+1][i],temp,res);
                vis[i]=0;
                temp.pop_back();
            }
            
        }
    }
}
int main()
{
     int n;
     cin>>n;
    
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++){
            cin>>a[i][j];
        }
  vector<pair<int,int> >temp;
  vector<pair<int,int> >res;
   dfs(n,0,0,temp,res);
   cout << fixed<<setprecision(2)<<MAX<< endl;
   for(int i=0;i<res.size();i++){
       cout<<res[i].first<<" "<<res[i].second<<endl;
   }
    system("pause");
    return 0;
}
发表于 2020-05-27 16:07:27 回复(0)
回溯加处理特殊情况
import java.util.*;
public class Main {

    static boolean[] visited;
    static double max = -1000;
    static int n;
    static double[][] nums;
    static List<int[]> relation = new ArrayList<>();
    static List<int[]> maxRelation = new ArrayList<>();

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        n = in.nextInt();
        nums = new double[n][n];
        visited = new boolean[n];
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < n; j++) {
                nums[i][j] = in.nextDouble();
            }
        }
        helper(0, 0);
        System.out.println(String.format("%.2f", max));
        for (int[] re: maxRelation) {
            System.out.println((re[0] + 1) + " " + (re[1] + 1));
        }
    }

    private static void helper(int row, double count) {
        // 这个时候计算最大值
        if (row == n) {
            if (count >= max) {
                if (count == 0) {
                    max = count;
                    List<int[]> list = new ArrayList<>();
                    for (int i = 0; i < n; i++) {
                        int[] temp = new int[2];
                        temp[0] = i;
                        temp[1] = i;
                        list.add(temp);
                    }
                    maxRelation = new ArrayList<>(list);
                } else {
                    max = count;
                    maxRelation.clear();
                    maxRelation = new ArrayList<>(relation);
                }
            }
            // 这个时候记录订单
            return;
        }
        for (int i = 0; i < n ; i++) {
            if (!visited[i]) {
                // 同时记录订单与司机的关系
                int[] relationArr = new int[2];
                relationArr[0] = row;
                relationArr[1] = i;
                relation.add(relationArr);
                visited[i] = true;
                helper(row + 1, count + nums[row][i]);
                relation.remove(relation.size() - 1);
                visited[i] = false;
            }
        }
    }

}

发表于 2020-04-25 22:37:30 回复(0)
dfs就完了
#include<bits/stdc++.h>
using namespace std;
vector<int>path, ret;
double max_ = -1;
void dfs(vector<vector<double>>& num, int cur, int status, double sum)
{
    if (cur == num.size())
    {
        if (sum > max_)
        {
            max_ = sum;
            ret = path;
        }
        return;
    }
    for (int i = 0; i < num.size(); i++)
    {
        if (status & (1 << i))continue;
        path.push_back(i + 1);
        dfs(num, cur + 1, status | 1 << i, sum + num[cur][i]);
        path.pop_back();
    }
}
int main()
{
    int n;
    cin >> n;
    vector<vector<double>>num(n, vector<double>(n));
    vector<vector<int>>path(n, vector<int>(n));
    iota(path[0].begin(), path[0].end(), 1);
    for (int i = 0; i < n; i++)
        for (int j = 0; j < n; j++)
            cin >> num[i][j];
    dfs(num, 0, 0, 0);
    cout << fixed<<setprecision(2)<<max_ << endl;
    for (int i = 0; i < ret.size(); i++)
        cout << i+1 << " " << ret[i] << endl;
    return 0;
}


发表于 2020-04-17 12:45:00 回复(0)
def assign_order(A: list):
    n = len(A)
    result = []
    m = [-float('inf')]
    visited = [[0] * n] * n
    def dfs(cur, n, t, res):
        if cur > n:
            sumt = sum(t)
            if sumt > m[0]:
                m[0] = sumt
                result.append(res)
            return 
        
        for i in range(n + 1):
            if not visited[cur][i]:
                visited[cur][i] = 1
                dfs(cur + 1, n, t + [A[cur][i]], res + [i + 1])
                visited[cur][i] = 0
                
    dfs(0, n - 1, [], [])
    print('{:.2f}'.format(m[0]))
    for i in range(n):
        print('{} {}'.format(i + 1, result[-1][i]))

n = int(input().strip())
matrix = []
for i in range(n):
    matrix.append(list(map(float, input().strip().split())))

assign_order(matrix)
通过90%,后面超时,用JAVA重写的话应该就过了。
发表于 2020-03-11 18:12:35 回复(0)