一、问题(UV10131\PC 111101)
[问题描述]
一些人认为,大象的体型越大,脑子越聪明。为了反驳这一错误观点,你想要分析一组大象的数据,找出尽量
多的大象组成一个体重严格递增但 IQ 严格递减的序列。
[输入]
输入包含若干大象的数据,每行一头大象,直到输入结束。每头大象的数据包括两个整数:第一个是以千克为
单位的体重,第二个是以整百为单位的 IQ 指数。两个整数均在 1 到 10000之间。输入最多包含 1000 头
大象。两头大象可能有相同的体重,或者相同的 IQ,甚至体重和 IQ 都相同。
[输出]
输出第一行应当包括一个整数 n,为找到的大象序列的长度。接下来的 n 行,每行包含一个正整数,表示一
头大象。用 W[i] 和 S[i] 表示输入数据中第 i 行的两个数,则若找到的这一序列为 a[1],a[2],
… ,a[n],则必须有:
W [a[1]] < W [a[2]] < … < W [a[n]] 和 S[a[1]] > S[a[2]] > … > S[a[n]]i
这里的 n 应当是最大可能值。所有不等关系均为严格不相等:体重严格递增,而 IQ 严格递减。
如果存在多组解,你的程序只需输出任何一个解。
[样例输入]
6008 1300
6000 2100
500 2000
1000 4000
1100 3000
6000 2000
8000 1400
6000 1200
2000 1900
[样例输出]
4
4
5
9
7
解释:符合题意的最长序列长度为4,按顺序是
第4行 1000 4000
第5行 1100 3000
第9行 2000 1900
第7行 8000 1400
二、分析
假设输入了n头大象的体重和IQ信息,题目要求找到一个序列,满足大象体重严格递增而IQ却严格递减,可以把问题转化为求图的一条路径:构造一个有向图,从i的j的边表示大象i、j满足题目条件,从而可以得到一个以有向无环图,并用矩阵DAGraph[i][j] 表示。为了找到符合条件的最长序列,可以用动态规划求解有向无环图的最长通路。设d[i]表示从节点i出发的最长路径长度,可以得到动态转移方程:d[i] = max(d[i] , d[j]+1) ,其中i到j是有一条边的。
三、代码
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
List<int[]>elephant = input(); //按行输入数据,遇空行结束
//将输入存入二维数组
int n = elephant.size();
int[][] weight_IQ = new int[n][2];
for(int i=0;i<n;i++)
{
int[] temp = new int[2];
temp=elephant.get(i);
weight_IQ[i][0]=temp[0];
weight_IQ[i][1]=temp[1];
}
int[][] DAGraph = buildDAG(weight_IQ,n); //建图
int []d = new int[n]; //d[i]表示从第i个点出发的最长路径长度
int ans=0; //记录总最长路径长度
for(int i=0;i<n;i++)
{
int t = df(i,d,n,DAGraph);
ans = ans>t?ans:t;
}
int begin=0; //起点位置
for(int i=0;i<n;i++)
if(d[i]==ans)
begin = i; //找出最长路径的起点位置
System.out.println(ans);
print(begin,n,DAGraph,d); //打印最长路径
}
private static List<int[]> input()
{
Scanner in = new Scanner(System.in);
String s = null;
List<int[]>elephant = new ArrayList<int[]>();
while(!(s=in.nextLine()).equals(""))
{
int[] temp = new int[2];
String[] str = s.split(" ");
temp[0]=Integer.parseInt(str[0]);
temp[1]=Integer.parseInt(str[1]);
elephant.add(temp);
}
return elephant;
}
private static int[][] buildDAG(int info[][],int n)
{
int[][] DAG = new int[n][n]; //DAG[i][j]表示大象i体重小于大象j且IQ大于大象j
for(int i=0;i<n;i++)
for(int j=0;j<n;j++)
{
if(info[i][0]<info[j][0]&&info[i][1]>info[j][1])
DAG[i][j]=1;
else
DAG[i][j]=0;
}
return DAG;
}
private static int df(int i,int d[],int n,int DAG[][])
{
if(d[i]>0) return d[i]; //如果已经有该点出发的最长路径长度,直接返回这个值
d[i]=1; //初始长度为1
for(int j=0;j<n;j++)
{
if(DAG[i][j]==1)
{
int t = df(j,d,n,DAG);
d[i] = d[i]>t+1?d[i]:t+1; //比较从i到j后路径如果变长,则选择较长的路径
}
}
return d[i];
}
private static void print(int begin,int n,int DAG[][],int d[])
{
int now = begin;
System.out.println(now+1);
for(int i=0;i<n;i++)
{
if(DAG[now][i]==1&&d[now]==d[i]+1)
{
System.out.println(i+1);
now=i;
} //只能找出其中一条最长路径
}
}
}