History Grading |
Background
Many problems in Computer Science involve maximizing some measure according to constraints.
Consider a history exam in which students are asked to put several historical events into chronological order. Students who order all the events correctly will receive full credit, but how should partial credit be awarded to students who incorrectly rank one or more of the historical events?
Some possibilities for partial credit include:
- 1 point for each event whose rank matches its correct rank
- 1 point for each event in the longest (not necessarily contiguous) sequence of events which are in the correct order relative to each other.
For example, if four events are correctly ordered 1 2 3 4 then the order 1 3 2 4 would receive a score of 2 using the first method (events 1 and 4 are correctly ranked) and a score of 3 using the second method (event sequences 1 2 4 and 1 3 4 are both in the correct order relative to each other).
In this problem you are asked to write a program to score such questions using the second method.
The Problem
Given the correct chronological order of n events as where denotes the ranking of event i in the correct chronological order and a sequence of student responses where denotes the chronological rank given by the student to event i; determine the length of the longest (not necessarily contiguous) sequence of events in the student responses that are in the correct chronological order relative to each other.
The Input
The first line of the input will consist of one integer n indicating the number of events with . The second line will containn integers, indicating the correct chronological order of n events. The remaining lines will each consist of n integers with each line representing a student's chronological ordering of the n events. All lines will contain n numbers in the range , with each number appearing exactly once per line, and with each number separated from other numbers on the same line by one or more spaces.
The Output
For each student ranking of events your program should print the score for that ranking. There should be one line of output for each student ranking.
Sample Input 1
4 4 2 3 1 1 3 2 4 3 2 1 4 2 3 4 1
Sample Output 1
1 2 3
Sample Input 2
10 3 1 2 4 9 5 10 6 8 7 1 2 3 4 5 6 7 8 9 10 4 7 2 3 10 6 9 1 5 8 3 1 2 4 9 5 10 6 8 7 2 10 1 3 8 4 9 5 7 6
Sample Output 2
6 5 10 9
解题思路:
最长公共子序列的应用.之前已经有过一些相对应的练习,不过这道题关于历史时间的时间排序.其中输入的序列并不一定代表着时间的顺序,而是代表着事件i发生的位置在哪里.
注意,给出的序列和一般理解上的出现次序是不同的。比如4 2 3 1,一般理解是第4个事件在第1位,第2个事件在第2位,第1个事件在第4位。但在这道题目,序列的含义是第1个事件在第4位,第2个事件在第2位,第4个事件在第一位。为方便计算,需要对输入的序列进行转换,使数字对号入座。比如一个正确的序列是:
正确答案 | 学生答案 | 含义 | |
输入的序列 | 3 1 2 4 9 5 10 6 8 7 | 2 10 1 3 8 4 9 5 7 6 | 历史事件发生在第几位 |
转换后的序列 | 2 3 1 4 6 8 10 9 5 7 | 3 1 4 6 8 10 9 5 7 2 | 发生了第几个历史事件 |
接下来就用转换后的序列进行计算。为了方便的看出学生答案的顺序是否正确,应该按照正确答案与1 2 ... 10的对应关系将学生答案再做一次转换。正确答案中第2个历史件事发生在第1位,那么学生答案中的2应转换为1;即3转换为2,以此类推。学生答案就转换为最终的序列为:
编号 | 1 2 3 4 5 6 7 8 9 10 |
序列 | 2 3 4 5 6 7 8 9 10 1 |
显而易见,这个序列的最长有序子串(非连序)的长度为9。要将输入的正确答案序列和学生答案序列都依次做上述转换就显得非常麻烦了,其实正确答案序列是无需转换的。假设正确答案序列为A,学生答案序列为B,则发生在第Bi位的历史事件的最终位置就应该是Ai。这样就可以一次完成全部转换。
接下来就是要找出最长有序子串。因为正确的顺序是由小至大,所以从最后一位开始遍例。依次找出每个数之后(含自身)的最长有序子串长度Mi,然后找最Mi中的最大值即为解。观查下面的序列:
编号 | 1 2 3 4 5 6 7 8 9 10 |
序列 | 5 8 3 7 6 1 9 2 10 4 |
因此在进行传入的参数应该是变换之后的数组。
对于如何将编号转化为序列号.很简单的操作:
for(int i=1; i<=n; i++) { cin>>loc;//事件i发生的位置loc x[loc]=i;// }
完整代码:
1 #include <bits/stdc++.h> 2 const int MAX=21; 3 int x[MAX]; 4 int y[MAX]; 5 int DP[MAX][MAX]; 6 int b[MAX][MAX]; 7 int n,i,j,loc; 8 9 using namespace std; 10 11 void work(int x[],int y[],int n) 12 { 13 memset(DP,0,sizeof(DP)); 14 for(i=1; i<=n; i++) 15 { 16 for(j=1; j<=n; j++) 17 { 18 if(x[i]==y[j]) 19 { 20 DP[i][j]=DP[i-1][j-1]+1; 21 } 22 else 23 DP[i][j]=max(DP[i-1][j],DP[i][j-1]); 24 } 25 } 26 cout<<DP[n][n]<<endl; 27 } 28 29 int main() 30 { 31 scanf("%d",&n); 32 for(int i=1; i<=n; i++) 33 { 34 cin>>loc; 35 x[loc]=i; 36 } 37 while(~scanf("%d",&loc))//avoid TLE 38 { 39 y[loc]=1; 40 for(int j=2; j<=n; j++) 41 { 42 cin>>loc; 43 y[loc]=j; 44 } 45 work(x,y,n); 46 memset(y,0,sizeof(y)); 47 } 48 return 0; 49 }