Part-Time Jobs

来源:互联网 发布:网络电影数据分析 编辑:程序博客网 时间:2024/06/05 16:48

时间限制:20000ms
单点时限:2000ms
内存限制:512MB

描述

Little Hu is taking paid part-time jobs in his college. There are N buildings in his college, numbered from 1 to N.These buildings are connected by M bi-directional paths in such a way that every pair of buildings is connected by these paths directly or indirectly. Moving from one building to another will cost Little Hu some time.

In the morning Little Hu receives Q jobs. Each job can be described with 5 integers, LiSiEiTiCiLi, the building number of the i-th job, describes the location to do the job. Si is the earliest time to start the job and Ei is the latest time to start the job. Ti is the time cost. Ci is the amount of money paid if Little Hu finished the job on time.

Little Hu starts at time 0 in building 1. He wants to maximize his total pay by carefully selecting jobs. Can you write a program to help him find the maximum total pay?

输入

The first line contains 3 integers NM and Q.

The following M lines describe the paths. Each line contains 3 integers UiVi and Wi indicating that there is path between building Ui and Vi and moving from Ui to Vi along the path, or vice versa, costs time Wi.

The following Q lines describe the jobs. Each line contains 5 integers LiSiEiTiCi.

For 20% of the data: 1 ≤ UiViLi ≤ N ≤ 5, 1 ≤ M ≤ 20, 1 ≤ Q ≤ 5

For 40% of the data: 1 ≤ UiViLi ≤ N ≤ 100, 1 ≤ M ≤ 1000, 1 ≤ Q ≤ 10

For 100% of the data: 1 ≤ UiViLi ≤ N ≤ 10000, 1 ≤ M ≤ 1000000, 1 ≤ Q ≤ 20

For 100% of the data: 0 ≤ Si ≤ Ei ≤ 10000, 1 ≤ TiWi ≤ 10000, 1 ≤ Ci ≤ 100, Ui ≠ Vi

输出

Output the maximum pay Little Hu can get.

样例输入
3 5 53 1 43 2 21 2 51 3 23 1 43 5 9 3 81 4 7 4 43 7 10 4 82 2 3 4 93 3 15 4 8
样例输出
24

惭愧,写了好几天没写出来,感觉这是自己写过的最长的code了,写代码的能力还是有待提高啊


首先,由于有Q件任务,所以完成任务的顺序一共有Q!种。

我们可以比较简单的判断一种顺序可不可行。例如先完成任务3,再完成任务1,最后完成任务2。首先我们一定沿着从Building 1到L3的最短路达到L3,这是到达L3最早的方式。比较当前时间和任务3的时间要求([S3, E3],花费T3):

  1. 当前时间 < S3,那么我们要等到S3开工,最早完成任务3的时间是S3+T3。
  2. 当前时间 > E3,那么我们来晚了,来不及完成任务3。任务3失败,不用再判断后面的任务。
  3. 否则,我们可以直接开工,最早完成任务3的时间是 当前时间+T3。

之后我们会沿着L3到L2的最短路到达任务2的地点L2,再进行以上的判断…… 直到所有任务都完成或者在某一步失败。这时可以计算获得的报酬。

这个直观的算法有两个问题: 1. 我们需要知道两个地点之间的最短路。 2. 时间复杂度还是有点高,Q!种顺序,每种顺序还要O(Q)判断。

对于第一个问题,由于学校中有N=10000个建筑,求出这些建筑两两之间的最短路复杂度很高。不过相比建筑数N,任务数Q很小,最大是20。实际上我们只用到从有任务的建筑到另一个有任务的建筑之间的最短路,以及从起点到一个有任务的建筑之间的最短路。这些我们关心的点最多有1+Q个。我们只需要以这1+Q个点为起点,在整个图上执行1+Q次单源最短路算法,就能求出这1+Q个点两两之间的最短路。这里单源最短路推荐选择SPFA,速度较快。

对于第二个问题,我们需要用到状态压缩动态规划。我们用一个整数S (0 <= S < 2^Q)来描述当前哪些任务是完成的,那些没有完成。具体来说,如果S的二进制表示的从低到高第i位是1表示,第i个任务是完成的;0表示第i个任务未完成。例如S=5,也即(101)2,表示第一个任务完成,第二个任务没完成,第三个任务完成,其余任务都没完成的状态。

我们用f[S][i]表示完成任务状态是S,并且最后完成的是第i个任务,最少花费的时间。如果我们不可能达到这种状态,则f[S][i]=-1。

通过枚举下一个任务j完成转移:f[S][i] -> f[S | 2^j][j] | S第j位不等于1。

最后我们检查所有可能的状态(S, i)。如果f[S][i]的值不是-1,就意味着我们可以通过某种顺序,完成S中的所有任务。这时报酬就是S中非0二进制位代表的任务报酬之和。

package h179;import java.util.Arrays;import java.util.Comparator;import java.util.HashSet;import java.util.PriorityQueue;import java.util.Queue;import java.util.Scanner;import java.util.Set;public class Main {public static void main(String[] args) {Scanner sc = new Scanner(System.in);int n=sc.nextInt(), m=sc.nextInt(), q=sc.nextInt();int[][] adj = new  int[1+n][1+n];for(int i=0; i<m; i++) {int s=sc.nextInt(), t=sc.nextInt(), d=sc.nextInt();if(adj[s][t]==0) {adj[s][t]=d;  adj[t][s]=d;} else {adj[s][t]=Math.min(adj[s][t], d);  adj[t][s]=Math.min(adj[s][t], d);}}int[][] tasks = new int[q+1][5];Set<Integer> taskLoc = new HashSet<Integer>();for(int i=1; i<=q; i++) {tasks[i][0]=sc.nextInt();tasks[i][1]=sc.nextInt();tasks[i][2]=sc.nextInt();tasks[i][3]=sc.nextInt();tasks[i][4]=sc.nextInt();taskLoc.add(tasks[i][0]);}int[][] dist = new int[1+Math.min(q, n)][1+Math.min(q, n)];for(int i:taskLoc)dist[i] = getSingleSP(i, n, adj);// check dist is symetricint[][] memo = new int[(int) Math.pow(2, 2+n)][1+q];for(int i=0; i<memo.length; i++)Arrays.fill(memo[i], -1);dfs(dist, memo, 0, new boolean[q+1], 0, tasks, 0);int ret = 0;for(int i=0; i<memo.length; i++) {for(int j=0; j<q; j++) {if(memo[i][j] != -1) {int bit=0, val=0, tmp=i;while(tmp>0) {if(tmp%2==0)val+=tasks[bit][4];tmp /= 2;bit++;}ret = Math.max(ret, val);}}}System.out.println(ret);}private static void dfs(int[][] dist, int[][] memo, int cur, boolean[] mark, int finishedTasks, int[][] tasks, int curTime) {if(memo[finishedTasks][cur] != -1)return ;for(int i=1; i<mark.length; i++) {int pos = tasks[i][0];if(dist[cur][pos]!=Integer.MAX_VALUE && !mark[pos] && (dist[cur][pos]+curTime<=tasks[i][2])) {mark[pos] = true;memo[finishedTasks][cur] = Math.max(dist[cur][pos]+curTime, tasks[i][1])+tasks[i][3];dfs(dist, memo, pos, mark, finishedTasks|(2<<i), tasks, Math.max(curTime, tasks[i][1])+tasks[i][3]);mark[pos] = false;}}}private static int[] getSingleSP(int s, int n, int[][] adj) {boolean[] collected = new boolean[n+1];final int[] dist = new int[n+1];Arrays.fill(dist, Integer.MAX_VALUE);dist[s] = 0;// not add to pq unless not Int_maxQueue<Integer> pq = new PriorityQueue<Integer>(n, new Comparator<Integer>(){@Overridepublic int compare(Integer i1, Integer i2) {return dist[i1]-dist[i2];}});pq.add(s);while(true) {if(pq.size() == 0)break;int minIdx = pq.remove();collected[minIdx] = true;for(int to=1; to<=n; to++) {if(adj[minIdx][to]!=0) {if(dist[to] > dist[minIdx] + adj[minIdx][to]) {dist[to] = dist[minIdx] + adj[minIdx][to];pq.add(to);}}}}return dist;}}


原创粉丝点击