【蓝桥备考-每周一题】越大与聪明?(DAG动态规划)

一、问题(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;
			}  //只能找出其中一条最长路径
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值