UVa 10131 Is Bigger Smarter? 最长公共子序列 yongmou

题意:给出不超过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;
}
原文地址:https://www.cnblogs.com/liyongmou/p/1774739.html