题意:给出不超过1000头大象的(重量,IQ),其中重量之间可能有相同的,IQ之间也可能有相同的,寻找重量严格递增,IQ严格递减的序列,输出其长度,和序列,序列以输入数据中的序号表示。
思路:第一步:对这些大象按照重量排序,排序时,只对索引排序就可以了,不需要动大象的实际数据,比如(600,300,400),对应索引是(1, 2,3)排序后是(2, 3,1)。
第二步:对IQ排序,这需要消去重复的。 我记得,见过一个算法题,如何对于0到n的若干整数以O(n)的时间排序?我的解答是:用一个数组appear[n],初始化为0,若数字m出现则appear[m]++,最后一个循环拷贝到一个数组中。并且这样做的,可以很容易消去重复的。
第三步:对上述两个序列求最长公共子序列。子问题填表。回溯找到最优解。
最后:消去序列中重量重复的,输出。
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAX 1000
struct elephant{
int w;
int s;
};
int n, m;
elephant elephants[MAX+1];
int a[MAX+1]; //索引
int b[MAX+1]; //大象IQ的降序排列
bool appear[10000+1]; //用于对大象IQ排序, 若出现了IQ为i的大象则in[i]置true
int sub[MAX+1], len;//子序列,长度
int dp[MAX+1][MAX+1];
char sel[MAX+1][MAX+1]; //dp时选择, 1表示相等,2左, 3上
//用于对重量排序的函数对象
struct cmp{
bool operator ()(int a, int b){
return (elephants[a].w < elephants[b].w);
}
};
//最长公共子序列
void LCS(){
int i, j;
for(i=0; i<=n; ++i)
dp[i][0] = 0;
for(j=0; j<=m; ++j)
dp[0][j] = 0;
for(i=1; i<=n; ++i)
for(j=1; j<=m; ++j){
if(elephants[a[i]].s == b[j]){
dp[i][j] = dp[i-1][j-1] + 1;
sel[i][j] = 1;
}
else if(dp[i-1][j] <= dp[i][j-1]){
dp[i][j] = dp[i][j-1];
sel[i][j] = 2;
}
else{
dp[i][j] = dp[i-1][j];
sel[i][j] = 3;
}
}
}
//回溯找到公共子序列
void backtrace(){
len = 0;
int i, j;
i=n, j=m;
while(i!=0 && j!=0){
if(sel[i][j] == 1){
++len;
sub[len] = a[i];
--i, --j;
}
else if(sel[i][j] == 2)
--j;
else
--i;
}
}
int main(){
// freopen("in", "r", stdin);
int w, s;
n=0;
while(scanf("%d %d", &w, &s) != EOF){
++n;
elephants[n].w = w;
elephants[n].s = s;
appear[s] = true;
a[n] = n;
}
sort(a+1, a+1+n, cmp());
m = 0;
for(int i=10000; i>=1; --i){
if(appear[i]){
m++;
b[m] = i;
}
}
LCS();
backtrace();
//消去重量相同的
int k, j;
k=1;
b[1] = sub[len];
for(j=len-1; j>=1; --j){
if(elephants[sub[j]].w == elephants[b[k]].w)
--len;
else{
++k;
b[k] = sub[j];
}
}
printf("%d\n", len);
for(k=1; k<=len; ++k)
printf("%d\n", b[k]);
return 0;
}