1、题目描述
多多制作人正在筹拍一部电影,需要招募一批演员。为了确保影片顺利拍摄,制作团队需要合理地分配每位演员的片酬,否则演员可能罢演。片酬的分配由团队成员共同商议决定,每位成员对演员的片酬标准有自己独特的评估意见,在团队讨论时,成员们可能会根据演员所饰演角色的杂性、表演难度、台词量等因素,提出不同的薪资调整建议。例如,某些成员认为某个角色的表演难度较大,应该给予更高的片酬;而另一些成员则认为某个角色的台词较多,也应该提高该演员的报酬。团队成员的意见可以通过会议提出,并且不同成员的意见有可能存在矛盾,每条意见以一对明确的优先顺序给出,例如,某个演员的片酬应该高于另一个演员的片酬。请你帮多多计算在满足所有制作团队成员的意见下,求出一个使得演员片酬总额最小的方案,每位演员的片酬必须至少为100元,且每次调整的增量为10元
输入描述
第一行为一个整数T, 表示共有T个测试教据(1 <= T<= 10)
每组测试教据:
第一行为两个整数n和m,分别表示招尊演员的总数和制作团队成员提出的意见数(1<=n<=10000,1<=m<=20000)
接下来的m行:
每行输入两个整数ai,bi 表示有制作团队成员认为演员ai的片酬应当比演员bi的片酬高
(1 <= ai, bi <= n)
输出描述
每组数据输出一个结果,每个结果占一行,如果满足不了所有制作团队成员的意见则输出-1
补充说明
对于20%的数据有:1 <=n<= 100,1 <= m <= 200
对于40%的数据有:1<=n<= 1000,1 <=m<= 2000
对于100%的数据有:1<=n<= 10000,1 <=m <= 20000
输入
1
7 4
7 2
4 6
2 5
7 5
输出
740
说明
演员7片酬需要比2,5高
演员4片酬需变比6高
演员2片酬需要比5高
因此总额最小的招算方案为:
演员 |演员1|演品2|演员3|演员4|演员5|演品6|演员7
[-------|-------|--------|--------|-------|--------|--------|-------|
片酬 | 100 | 110 | 100 | 110 | 100 | 100 | 120 |
总计片酬740
2、解题思路
要解决这个问题,我们需要根据给定的优先关系(即某些演员的片酬必须高于其他演员的片酬),为所有演员分配片酬,使得总片酬最小,同时满足每位演员的片酬至少为100元,且每次调整的增量为10元。如果无法满足所有优先关系(例如存在循环依赖),则返回-1。
方法思路
-
拓扑排序:我们需要将演员之间的优先关系建模为有向图,其中边
ai -> bi
表示演员ai
的片酬必须高于演员bi
。使用拓扑排序来检查图中是否存在环,如果存在环,则无法满足所有优先关系,返回-1。 -
动态分配片酬:在拓扑排序的过程中,为每个演员分配片酬。每个演员的片酬应比其所有后继节点的片酬至少高10元。初始时,所有演员的片酬设为100元。在拓扑排序中,每当处理一个节点时,其片酬应设置为所有前驱节点片酬的最大值加10元。
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int T = scanner.nextInt();
for (int t = 0; t < T; t++) {
int n = scanner.nextInt();//演员的总数
int m = scanner.nextInt();//制作团队成员提出的意见数
//1.//构建图,使用邻接表graph存储优先关系,并统计每个节点入度inDegree
List<List<Integer>> graph = new ArrayList<>();
for (int i = 0; i <= n; i++) {
graph.add(new ArrayList<>());
}
int[] inDegree = new int[n + 1]; // 因为下标从1开始
for (int i = 0; i < m; i++) {
int a = scanner.nextInt();
int b = scanner.nextInt();
graph.get(b).add(a);
inDegree[a]++;
}
//使用队列进行拓扑排序
Queue<Integer> queue = new LinkedList<>();
//初始化所有演员片酬为100
int[] salary = new int[n + 1];
Arrays.fill(salary, 100);
for (int i = 1; i <= n; i++) {
if (inDegree[i] == 0) { // 入度为0,则入队
queue.offer(i);
}
}
int processed = 0;
while (!queue.isEmpty()) {
int u = queue.poll();
processed++;
for (int v : graph.get(u)) {
if (salary[v] <= salary[u]) {
salary[v] = salary[u] + 10;
}
inDegree[v]--;
if (inDegree[v] == 0) {
queue.offer(v);
}
}
}
if (processed != n) { //检查遍历得到的processed 是否与演员总数一致,如果不一致表示可能出现环,返回-1
System.out.println(-1);
} else {
int total = 0;
for (int i = 1; i <= n; i++) {
total += salary[i];
}
System.out.println(total);
}
}
}
代码解释
-
输入处理:读取测试用例数量
T
,然后对于每个测试用例,读取演员数量n
和意见数量m
。 -
构建图:使用邻接表
graph
存储优先关系,并统计每个节点的入度inDegree
。 -
拓扑排序:使用队列进行拓扑排序。初始化所有演员的片酬为100元。处理入度为0的节点,调整其邻接节点的片酬(至少比当前节点高10元),并减少邻接节点的入度。如果邻接节点的入度变为0,加入队列。
-
检查环:如果处理的节点数量不等于总演员数,说明存在环,输出-1。否则,计算并输出总片酬。
这种方法确保在满足所有优先关系的情况下,总片酬最小,且时间复杂度主要由拓扑排序决定,为O(n + m),适用于给定的约束条件。