看旗派舟
拼多多 9月6号 笔试真题 第四题
拼多多真题目录点击查看: 拼多多 春招&秋招 笔试真题题库目录|笔试题库 + 算法考点详解
题目内容
江上两座水寨要按旧例比旗。我方与对方各备 m m m 艘快艇。寨中留下一张对阵册:我方第 i i i 艘对上对方第 j j j 艘时,若册上为 1 则我方得 1 分,若为 -1 则我方得 -1 分。比旗共 m m m 轮,每轮对方先从自己尚未出列的艇里派出一艘;我方看清来艇后,再从自己尚未出列的艇里派一艘应战。两艘艇对阵一次后都不再上场。
对方希望我方总分尽量低,我方希望总分尽量高,双方都按最优策略行动。胜负可以互相克制、甚至成环,并不存在绝对最强的一艘艇。请对每一场比旗,求出我方最终能拿到的总分。
约束:
1≤ \le ≤ q q q ≤ \le ≤ 10 10 101≤ \le ≤ m m m ≤ \le ≤ 10 2 10^2 102- 对阵册每个数只可能是
1或-1
输入描述
第一行一个整数 q q q(1 ≤ \le ≤ q q q ≤ \le ≤ 10 10 10),表示比旗场数。
随后是 q q q 场,每场格式如下:
- 第一行一个整数 m m m(
1≤ \le ≤ m m m ≤ \le ≤ 10 2 10^2 102),表示每方快艇数量 - 接下来 m m m 行,每行 m m m 个整数 g i , 0 , g i , 1 , ... , g i , m − 1 g_{i,0},g_{i,1},\ldots,g_{i,m-1} gi,0,gi,1,...,gi,m−1(每个为
1或-1),表示我方第 i i i 艘对对方各艘的结果
输出描述
输出 q q q 行,每行一个整数,表示该场在双方最优策略下我方的总分。
样例1
输入
2
1
-1
2
1 -1
1 1
输出
-1
2
说明
- 第一场只有一艘艇,册上是
-1,我方只能得-1分。 - 第二场我方两艘都能在合适搭配下取胜,最大匹配为
2,总分 $2\times2-2=2。
样例2
输入
1
3
1 1 -1
1 -1 -1
-1 -1 1
输出
3
说明
三对都能配成我方获胜:例如 ( 0 , 1 ) (0,1) (0,1)、 ( 1 , 0 ) (1,0) (1,0)、 ( 2 , 2 ) (2,2) (2,2)。场数等于艇数,总分 $2\times 3 - 3 = 3。
样例3
输入
1
3
1 1 -1
-1 -1 -1
1 -1 -1
输出
1
说明
我方最多赢 2 场(第二艘对谁都输),总分 $2\times 2 - 3 = 1。
题解
思路
解题思路: 匈牙利算法
- 把"我方艇"和"对方艇"看成二分图,只有对阵结果为
1时连边,求这个二分图的最大匹配。答案为2 × 最大匹配数 - m - 把我方艇作为左边节点,对方艇作为右边节点。寻找最大
g[i][j] == 1的最大匹配数量k。寻找最大匹配数量采用匈牙利算法。 - 维护
match[j]表示当前对方第j艘艇匹配到了我方哪艘艇。对于每一艘我方艇i:- 尝试寻找一个结果为
1的对方艇j - 如果
j没有匹配,直接匹配. - 如果
j已经匹配给其他我方艇,则尝试让原来的我方艇换一个位置. - 如果能成功找到增广路,匹配数
+1
- 尝试寻找一个结果为
- 算法总体时间复杂度为
O(m^3)
C++
cpp
#include <bits/stdc++.h>
using namespace std;
int m;
vector<vector<int>> g;
vector<int> match;
vector<bool> vis;
// 寻找增广路
bool dfs(int u) {
for (int v = 0; v < m; v++) {
if (g[u][v] != 1 || vis[v]) {
continue;
}
vis[v] = true;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
int solve() {
match.assign(m, -1);
int maxMatch = 0;
for (int i = 0; i < m; i++) {
vis.assign(m, false);
if (dfs(i)) {
maxMatch++;
}
}
// 得分 maxMatch - (m - maxMatch)
return 2 * maxMatch - m;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int q;
cin >> q;
while(q --) {
cin >> m;
g.assign(m, vector<int>(m));
for (int i = 0; i < m; i++) {
for (int j = 0; j < m; j++) {
cin >> g[i][j];
}
}
cout << solve() << endl;
}
return 0;
}
java
java
import java.io.*;
import java.util.*;
public class Main {
static int m;
static int[][] g;
static int[] match;
static boolean[] vis;
// 寻找增广路
static boolean dfs(int u) {
for (int v = 0; v < m; v++) {
if (g[u][v] != 1 || vis[v]) {
continue;
}
vis[v] = true;
if (match[v] == -1 || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
static int solve() {
match = new int[m];
Arrays.fill(match, -1);
int maxMatch = 0;
for (int i = 0; i < m; i++) {
vis = new boolean[m];
if (dfs(i)) {
maxMatch++;
}
}
// 得分 maxMatch - (m - maxMatch)
return 2 * maxMatch - m;
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int q = Integer.parseInt(br.readLine().trim());
while (q-- > 0) {
m = Integer.parseInt(br.readLine().trim());
g = new int[m][m];
for (int i = 0; i < m; i++) {
StringTokenizer st = new StringTokenizer(br.readLine());
for (int j = 0; j < m; j++) {
g[i][j] = Integer.parseInt(st.nextToken());
}
}
System.out.println(solve());
}
}
}
python
python
import sys
m = 0
g = []
match = []
vis = []
# 寻找增广路
def dfs(u):
for v in range(m):
if g[u][v] != 1 or vis[v]:
continue
vis[v] = True
if match[v] == -1 or dfs(match[v]):
match[v] = u
return True
return False
def solve():
global match, vis
match = [-1] * m
max_match = 0
for i in range(m):
vis = [False] * m
if dfs(i):
max_match += 1
# 得分 maxMatch - (m - maxMatch)
return 2 * max_match - m
def main():
global m, g
input = sys.stdin.readline
q = int(input())
while q > 0:
q -= 1
m = int(input())
g = []
for _ in range(m):
g.append(list(map(int, input().split())))
print(solve())
if __name__ == "__main__":
main()
javascript
js
const readline = require('readline');
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
const input = [];
rl.on('line', line => {
input.push(line.trim());
});
rl.on('close', () => {
let index = 0;
let m = 0;
let g = [];
let match = [];
let vis = [];
// 寻找增广路
function dfs(u) {
for (let v = 0; v < m; v++) {
if (g[u][v] !== 1 || vis[v]) {
continue;
}
vis[v] = true;
if (match[v] === -1 || dfs(match[v])) {
match[v] = u;
return true;
}
}
return false;
}
function solve() {
match = new Array(m).fill(-1);
let maxMatch = 0;
for (let i = 0; i < m; i++) {
vis = new Array(m).fill(false);
if (dfs(i)) {
maxMatch++;
}
}
// 得分 maxMatch - (m - maxMatch)
return 2 * maxMatch - m;
}
const q = Number(input[index++]);
for (let caseNum = 0; caseNum < q; caseNum++) {
m = Number(input[index++]);
g = new Array(m);
for (let i = 0; i < m; i++) {
g[i] = input[index++].split(/\s+/).map(Number);
}
console.log(solve());
}
});
Go
go
package main
import (
"bufio"
"fmt"
"os"
)
var m int
var g [][]int
var match []int
var vis []bool
// 寻找增广路
func dfs(u int) bool {
for v := 0; v < m; v++ {
if g[u][v] != 1 || vis[v] {
continue
}
vis[v] = true
if match[v] == -1 || dfs(match[v]) {
match[v] = u
return true
}
}
return false
}
func solve() int {
match = make([]int, m)
for i := 0; i < m; i++ {
match[i] = -1
}
maxMatch := 0
for i := 0; i < m; i++ {
vis = make([]bool, m)
if dfs(i) {
maxMatch++
}
}
// 得分 maxMatch - (m - maxMatch)
return 2*maxMatch - m
}
func main() {
in := bufio.NewReader(os.Stdin)
out := bufio.NewWriter(os.Stdout)
defer out.Flush()
var q int
fmt.Fscan(in, &q)
for ; q > 0; q-- {
fmt.Fscan(in, &m)
g = make([][]int, m)
for i := 0; i < m; i++ {
g[i] = make([]int, m)
for j := 0; j < m; j++ {
fmt.Fscan(in, &g[i][j])
}
}
fmt.Fprintln(out, solve())
}
}