运动员最佳匹配问题 | 回溯:N排列(最大剪枝)

羽毛球队有男女运动员各n人. 给定2个n*n矩阵P和Q. P[i][j]是男运动员i与女运动员j配混合双打的男运动员竞赛优势; Q[i][j]是女运动员i与男运动员j配混合双打的女运动员竞赛优势. 由于技术配合和心理状态等各种因素影响, P[i][j]不一定等于Q[j][i]. 男运动员i和女运动员j配对的竞赛优势是P[i][j]*Q[j][i]. 设计一个算法, 计算男女运动员最佳配对法, 使得各组男女双方竞赛优势的总和达到最大.

数据输入:第1行有一个正整数n(1<=n<=12), 接下来2n行是P和Q

结果输出:最佳配对的各组男女双方竞赛优势总和 

 

思路如下:

  • mymind
    • 抽象出来感觉是两块矩阵PQ 从P一行选一个找到Q中对应的值进行乘法和累加得到的值 每一行都互相制约 不能选相同的列
      • 感觉可以一行一行选下去 选不同的列
      • 用完就标记?每次也从第一个出发找第一个没标记的 下层都找完了才改
      • 最后留下的是
    • 给我的感觉是要一个个地匹配得到不同的结果,进行比较得到最大的结果
    • 有点深搜那味 会不会是广搜?
    • 扫描完所有?破案了我觉得就是深搜,因为有回溯这一步、
  • 题解
    • 好牛 不同层是不同的男运动员 每一层对应的G是不同的女运动员
      • 因为是互相制约的 不可以选同一个的那种感觉、(就像小木棍拼了不能再用,只是每一行代表不同的男运动员——要用树!
      • 然后是dfs嘛?!yesyes
    • 问题
      • c++里的INT_MIN 素什么?
        • 整型的下限INT_MAX = 2^31-1,INT_MIN= -2^31.
      • 看dalao的剪枝还是有点蒙蔽还是得加强
    • 回溯
      • 暴力枚举——回溯法 N排列的回溯模型
      • 首先将第 i 位男运动员与第 j 位女运动员的优势计算到二维数组 pq[ i ][ j ] 中,然后再在这个二维数组选 n 个组合(组合即对应每个点(i , j)),要求 n 个点不可同行或同列,将 n 个这样子符合条件的点求和,记录最大值。
        • 是的我就是这么想的
        • woc它的意思应该是先处理算出不同的结果之后再进行回溯法暴搜woc比我想的直接干会简单很多
        • 总之就是先建立一个数组存总的优势。
        • 和扫描最大的区别就是不能选同一个女运动员,所以用树
      • 创建数组记录女运动员是否被选择过
      • 怎么存的就怎么搜
      • 感觉就是层数就是步骤,就是dfs的参数。dfs里的循环就是女运动员,一个个去试去匹配,不能用重复的就要标记
      • 比较特别的地方就是它在深搜的时候如果不用这个人也就是退回的时候,sum会减去这个值

    • 剪枝
      • 最大问题的剪枝操作?——应该是一类问题?
      • 预先计算好1~i的匹配的最大情况?——存好贪心计算的最优解
      • 在第i层回溯中,前面的男运动员是已经安排好的?后面的优势不可能超过pre[n]-pre[i-1]?——意思是找前面的男运动员的最优解,因为是贪心其实可以无所谓条件 pre[n]其实是一种叠加态(前面n个全用贪心算出来的最优总和),为了避免每次都要一直累加,所以直接以终态的形式出现
      • 若当前优势加上pre[n]-pre[i-1]也就是最大的可能情况,都不可能超过已经记录的最大值,就没必要继续了
        • 是不是每一次都要判断一下,如果后续的最大值加上当前的sum都无法超过当前最大值,那么就没必要继续 就return换一个
        • 那怎么确定pre呢?
          • 贪心获取
          • 真的很妙,就是因为贪心是局部最优解,所以一定大于等于实际情况,如果(加上)贪心都达不到最优,就肯定不是最优
          • 肯定无所谓有没有重复利用呀 都说了是贪心嘛
          • pre[i]+=pre[i-1];这是为什么??不是已经确定了每一行的最大值为pre吗 为什么要加前一个的?
            • 我知道了,就是pre它存的是累加的感觉,pre[n]指的是n的时候,前面的所有的最优的总和,所以是sum+pre[n]-pre[t-1],pre[n]-pre[t-1]就是从t开始一直到n的贪心算出来的最优总和,否则每次还要进行叠加(把后续都加入),这就狠好
#include <bits/stdc++.h>
using namespace std;

int n;
int p[13][13],q[13][13];
int Max = 0;
int sum = 0;
int data[13][13];
int maxSum[21];//记录每一轮的优势?
int pre[13];
int vis[13];
void dfs(int t)//深搜
{
    if(t>n)//退出条件!
    {
        Max = max(Max,sum);//比较最大值和过程值,进行记录,是不是就不用开一个数组去记录
        return;
    }
    if(sum+pre[n]-pre[t-1]<Max)
        return;//加上贪心都小于最大值就直接退回了
    //这就是剪枝!!
    for(int j = 1;j<=n;j++)
    {
        if(vis[j]==1)continue;
        vis[j] = 1;
        sum +=data[t][j];//第t步其实就是第t层
        dfs(t+1);
        vis[j] = 0;
        sum-=data[t][j];//退出的时候记得删
    }
}

int main()
{
    scanf("%d",&n);
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
            scanf("%d",&p[i][j]);
    }
    for(int i = 1;i<=n;i++)
    {
        for(int j = 1;j<=n;j++)
            scanf("%d",&q[i][j]);
    }
    for(int i =1;i<=n;i++)
        for(int j =1;j<=n;j++)
        data[i][j] = p[i][j]*q[j][i];

    //贪心
    for(int i = 1;i<=n;i++){
        for(int j = 1;j<=n;j++)
            pre[i]=max(pre[i],data[i][j]);
        pre[i]+=pre[i-1];//为什么
    }


    dfs(1);

    printf("%d\n",Max);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值