题目## 题目
解题思路
这是一个最短路径和状态压缩动态规划结合的问题。具体要求:
- n n n 个城市之间有无向边相连,边权表示距离
- 2 k 2k 2k 个重要城市需要两两配对,形成 k k k 对友好城市
- 每对友好城市的代价是它们之间的最短路径长度
- 求所有可能的配对方案中,总代价最小的值
解决方案:
- 使用Floyd-Warshall算法计算任意两点间的最短路径
- 使用状态压缩DP求解最小完美匹配问题
- DP状态表示已匹配的城市集合,转移时枚举新的一对城市
代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int INF = 1e9;
const int MAXN = 100;
class Solution {
private:
int n, k;
int dist[MAXN][MAXN];
vector<int> important_cities;
// Floyd-Warshall算法计算最短路
void floydWarshall() {
for(int k = 0; k < n; ++k)
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
if(dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
// 状态压缩DP求最小匹配代价
int minMatchingCost() {
int size = important_cities.size();
vector<int> dp(1 << size, INF);
dp[0] = 0;
for(int mask = 0; mask < (1 << size); ++mask) {
int count = __builtin_popcount(mask);
if(count % 2 != 0) continue;
for(int i = 0; i < size; ++i) {
if(mask & (1 << i)) continue;
for(int j = i + 1; j < size; ++j) {
if(mask & (1 << j)) continue;
int new_mask = mask | (1 << i) | (1 << j);
int city1 = important_cities[i];
int city2 = important_cities[j];
dp[new_mask] = min(dp[new_mask],
dp[mask] + dist[city1][city2]);
}
}
}
return dp[(1 << size) - 1];
}
public:
int solve() {
cin >> n;
// 读入距离矩阵
for(int i = 0; i < n; ++i) {
for(int j = 0; j < n; ++j) {
cin >> dist[i][j];
if(dist[i][j] == -1)
dist[i][j] = INF;
}
}
// 读入重要城市
cin >> k;
important_cities.resize(2*k);
for(int i = 0; i < 2*k; ++i) {
cin >> important_cities[i];
important_cities[i]--; // 转为0-based索引
}
floydWarshall();
return minMatchingCost();
}
};
int main() {
Solution solution;
cout << solution.solve() << endl;
return 0;
}
import java.util.*;
public class Main {
static final int INF = 1000000000;
static final int MAXN = 100;
static class Solution {
int n, k;
int[][] dist = new int[MAXN][MAXN];
List<Integer> importantCities = new ArrayList<>();
void floydWarshall() {
for(int k = 0; k < n; ++k)
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j)
if(dist[i][k] + dist[k][j] < dist[i][j])
dist[i][j] = dist[i][k] + dist[k][j];
}
int minMatchingCost() {
int size = importantCities.size();
int[] dp = new int[1 << size];
Arrays.fill(dp, INF);
dp[0] = 0;
for(int mask = 0; mask < (1 << size); ++mask) {
int count = Integer.bitCount(mask);
if(count % 2 != 0) continue;
for(int i = 0; i < size; ++i) {
if((mask & (1 << i)) != 0) continue;
for(int j = i + 1; j < size; ++j) {
if((mask & (1 << j)) != 0) continue;
int newMask = mask | (1 << i) | (1 << j);
int city1 = importantCities.get(i);
int city2 = importantCities.get(j);
dp[newMask] = Math.min(dp[newMask],
dp[mask] + dist[city1][city2]);
}
}
}
return dp[(1 << size) - 1];
}
int solve(Scanner sc) {
n = sc.nextInt();
for(int i = 0; i < n; ++i)
for(int j = 0; j < n; ++j) {
dist[i][j] = sc.nextInt();
if(dist[i][j] == -1)
dist[i][j] = INF;
}
k = sc.nextInt();
for(int i = 0; i < 2*k; ++i)
importantCities.add(sc.nextInt() - 1);
floydWarshall();
return minMatchingCost();
}
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Solution solution = new Solution();
System.out.println(solution.solve(sc));
}
}
class Solution:
def __init__(self):
self.INF = 10**9
self.n = 0
self.k = 0
self.dist = []
self.important_cities = []
def floyd_warshall(self):
for k in range(self.n):
for i in range(self.n):
for j in range(self.n):
if self.dist[i][k] + self.dist[k][j] < self.dist[i][j]:
self.dist[i][j] = self.dist[i][k] + self.dist[k][j]
def min_matching_cost(self):
size = len(self.important_cities)
dp = [self.INF] * (1 << size)
dp[0] = 0
for mask in range(1 << size):
count = bin(mask).count('1')
if count % 2 != 0:
continue
for i in range(size):
if mask & (1 << i):
continue
for j in range(i + 1, size):
if mask & (1 << j):
continue
new_mask = mask | (1 << i) | (1 << j)
city1 = self.important_cities[i]
city2 = self.important_cities[j]
dp[new_mask] = min(dp[new_mask],
dp[mask] + self.dist[city1][city2])
return dp[(1 << size) - 1]
def solve(self):
self.n = int(input())
self.dist = []
for _ in range(self.n):
row = list(map(int, input().split()))
self.dist.append([self.INF if x == -1 else x for x in row])
self.k = int(input())
self.important_cities = list(map(lambda x: int(x)-1, input().split()))
self.floyd_warshall()
return self.min_matching_cost()
def main():
solution = Solution()
print(solution.solve())
if __name__ == "__main__":
main()
算法及复杂度
- 算法:Floyd-Warshall + 状态压缩DP
- 时间复杂度: O ( n 3 + k 2 ⋅ 2 2 k ) \mathcal{O}(n^3 + k^2 \cdot 2^{2k}) O(n3+k2⋅22k) - Floyd-Warshall算法 O ( n 3 ) O(n^3) O(n3),状态压缩DP部分 O ( k 2 ⋅ 2 2 k ) O(k^2 \cdot 2^{2k}) O(k2⋅22k)
- 空间复杂度: O ( n 2 + 2 2 k ) \mathcal{O}(n^2 + 2^{2k}) O(n2+22k) - 距离矩阵 O ( n 2 ) O(n^2) O(n2),DP数组 O ( 2 2 k ) O(2^{2k}) O(22k)
题解
##题目难度:简单难度
##知识点:数学逻辑、数组
解析问题:在分析问题时,用a[i]保存每个厨师的评分,num[i]保存每个厨师的奖金。
在解题时,主要有以下思路。
第一:从左向右比较,如果右边的数比左边的数大,则右边给的奖金多1K
第二:从右往左比较,如果左边的数比右边的数大,则左边给的奖金取max(左边奖金,右边奖金+1)
以示例数据为例,整个遍历的流程如下。
| 遍历 | 评分 | 原奖金 | 变化后奖金 |
|---|---|---|---|
| 从左到右 | 60 76 66 76 85 55 61 71 84 62 | 1 1 1 1 1 1 1 1 1 1 | 1 2 1 2 3 1 2 3 4 1 |
| 从右到左 | 60 76 66 76 85 55 61 71 84 62 | 1 2 1 2 3 1 2 3 4 1 | 1 2 1 2 3 1 2 3 4 1 |
整个过程遍历两次遍历,时间复杂度为O(N)
#include <bits stdc++.h>
using namespace std;
int main()
{
int n;
cin>>n;
//a[]保存每个厨师的评分,num[]保存每个厨师的奖金
int a[n];
int num[n];
for(int i=0;i<n;i++) { int x; cin>>x;
a[i]=x;
num[i]=1;
}
//从左向右比较,如果右边的数比左边的数大,则右边给的奖金多1K
for(int i=0;i<n-1;i++) if(a[i]<a[i+1]) num[i+1]="num[i]+1;" 从右往左比较,如果左边的数比右边的数大,则左边给的奖金取max(左边奖金,右边奖金+1) for(int j="n;j">1;j--)
if(a[j-2]>a[j-1])
num[j-2]=max(num[j-2], num[j-1]+1);
int sum=0;
for(int k=0;k</n-1;i++)></n;i++)></bits>
1460

被折叠的 条评论
为什么被折叠?



