问题描述
寂静的星期三…………
Nicole半夜去机房刷完题后,突然发现tech的U盘没有带走……
果然,上面有“动物世界”“入党申请书”“毛主席选集”“金刚葫芦娃”“英语听力材料”“走进科学”等很可疑的压缩包,但是狡猾的tech都设了密码。
历尽千辛万苦,Nicole发现一个文本文档,里面有很多数。
确切的说,一共有N行,每一行有M个数字。这些数字都是1到M的一个排列。
只是这些数全部都是有规律的,最后的密码肯定是这M列数字里面的某一列。而这一列的序号,是这N行数字的最长上升公共子序列的长度!!当然,你只需要告诉Nicole这个长度即可。
输入格式
第一行2个空格隔开的正整数N和M
接下来N行,每行M个数,保证这M个数全部是1~M的一个排列。
输出格式
一个数,如题目所述。
样例输入
2 7
1 2 3 5 7 6 4
1 7 2 6 3 4 5
样例输出
4
提示
数据范围:
对于100%的数据,有N<=100,M<=200
说明:
1 2 3 4和1 2 3 5均可。
解析:
• 抓住重点:
• 每个序列都是1到N的排列!
• 每个数在每个序列中只出现一次!
• 设定状态:f[x]表示以数字x结尾的最长上升公共子序列长度
• 状态转移:f[x] = f[y] + 1,其中1≤y≤x-1,且每个序列中y都在x左边
• 用pos[i][x]记录第i个序列中x的位置,则i=1,2,...,m时都要pos[i][y]<pos[i][x]
• 边界情况:如果找不到符合条件的y,则f[x]=1 • 最终答案:max(f[1],f[2],...,f[n])
• 时间复杂度𝑂 (n2m)
• 判断y能否转移到x,需要𝑂(m) 的时间,整个DP需要判断𝑂 (n*n) 次
代码:
#include <bits/stdc++.h>
using namespace std;
int p[1005][1005],n,m,f[1000];
int a[3000],b[3000],x,k;
int ans;
int main() {
cin>>n>>m;
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
cin>>x;
p[i][x]=j;
}
}
for(int i=1; i<=m; i++) {
f[i]=1;
for(int j=i-1; j>=1; j--) {
if(f[i]<=f[j]) {
bool fg=1;
for( k=1; k<=n; k++) {
if(p[k][j]>p[k][i]) {
fg=0;
}
}
if(fg==1) {
f[i]=f[j]+1;
}
}
}
ans=max(ans,f[i]);
}
cout<<ans<<endl;
}
稍后会完善思路