Time limit: 3.000 seconds
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 1, 2, ..., n as c1, c2, ..., cn where 1 ≤ ci ≤n denotes the ranking of event i in the correct chronological order and a sequence of student responses r1, r2, ..., rn where 1 ≤ ri ≤n 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 2 ≤ n ≤ 20. The second line will contain n 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 [1 ... n], 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
Analysis
注意,給出的序列和一般理解上的出現次序是不同的。比如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 |
演算法步驟如下:
- 4在第10位,顯然M10 = 1;
- M9 = 1;
- 2在第8位,後面只有4比它大,則M8 = M10 + 1 = 2;M7 = M9 + 1 = 2;
- 1在第6位,後面所有數都比它大,但M7到M10中最大值為M7=M8=2,則M6 = M7(或M8) + 1 = 3;
- M5 = M7 + 1 = 3;
- M4 = M7 + 1 = 3;
- M3 = M4(或M5) + 1 = 4;
- M2 = M7 + 1 = 3;
- M1 = M2(或M5) + 1 = 4;
至此可知,該序列的最長有序子串長度為4。
Solution
#include <iostream>using namespace std;int main(void) {int aTbl[20], aAns[20], aMax[20], nLen, nVar, i = 0;for (cin >> nLen; i < nLen && cin >> nVar; aTbl[i++] = nVar);while (true) {for (i = 0; i < nLen && cin >> nVar; aAns[nVar - 1] = aTbl[i++]);if (i != nLen) {break;}memset(aMax, 0, sizeof(aMax));for (nVar = 0, i = nLen - 1; i >= 0; --i) {for (int j = i + 1; j < nLen; ++j) {if (aAns[j] > aAns[i] && aMax[j] > aMax[i]) {aMax[i] = aMax[j];}}nVar = ++aMax[i] > nVar ? aMax[i] : nVar;}cout << nVar << endl;}return 0;}