题目
计算机科学中的许多问题涉及有约束的最优化问题。
考虑一个要求学生按时间顺序排序历史事件的历史考试。排序正确的学生将获得满分,但是只对了一部分的学生要怎么给分呢?
有以下一些可能:
1. 和正确答案对应相同的事件个数为其分数
2. 和正确答案相似程度最大的子序列(不要求连续)的长度为其分数。
举个例子,如果4个事件的正确顺序是1 2 3 4,那么答案1 3 2 4按第一种方法将获得2分(1和4事件对应上了);按第二种方法将获得3分(1 2 4之间的顺序是对的,或者1 3 4)。
为了最大化学生的成绩,减少他们对评分标准的不满,老师决定选用第2种方案作为评分标准。请你写一个程序实现它。
给定n个事件的正确顺序用c1,c2,⋯,cn表示,其中ci为第i个事件的按时间顺序的名次。
给定学生的答案r1,r2,⋯,rn,**其中rn表示学生认为第i个事件应该的排名是多少。
输出按第二种方案的评分。
输入
输入第一行一个整数n(2≤n≤20),表示历史事件的个数。
第二行包含n个整数
接下来每行n个整数表示多个学生的
EOF结束。
输出
对每个学生,输出一行一个整数,表示该学生的得分。
注意
请注意关键字眼:顺序和名次。
样例输入1
4
4 2 3 1
1 3 2 4
3 2 1 4
2 3 4 1
样例输出1
1
2
3
样例输入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
样例输出2
6
5
10
9
题解
很显然,将名次转换为原来的历史事件的id的话,与标准答案的最长公共子序列的长度就是答案,但是本题比较特别,原序列和新序列都不重复不缺漏地包含[1..n]的所有数字,因此本题可以转化为最长上升子序列解决。
#include <cstdio>
#include <cstring>
#include <algorithm>
#define FOR(i,j,k) for(i=j;i<=k;++i)
using namespace std;
int f[32], a[32], dp[32];
int main() {
int n, i, j, k, ans;
scanf("%d", &n);
FOR(i,1,n) scanf("%d", &k), f[i] = k;
while (1) {
FOR(i,1,n)
if (scanf("%d", &k) == EOF)
return 0;
else
a[k] = f[i];
memset(dp, 0, sizeof dp);
dp[1] = 1; a[0] = 0; ans = 0;
FOR(i,2,n) FOR(j,0,i-1)
if (a[i] > a[j])
dp[i] = max(dp[i], dp[j] + 1);
FOR(i,1,n) ans = max(ans, dp[i]);
printf("%d\n", ans);
}
return 0;
}