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

题意:给出不超过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/archive/2010/07/10/1774739.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值