• CJOJ 1070 【Uva】嵌套矩形(动态规划 图论)


    CJOJ 1070 【Uva】嵌套矩形(动态规划 图论)

    Description

    有 n 个矩形,每个矩形可以用两个整数 a, b 描述,表示它的长和宽。矩形 X(a, b) 可以嵌套在矩形 Y(c, d) 中当且仅当 a<c, b<d,或者 b<c, a<d(相当于把矩形 X 旋转了 90°)。例如 (1, 5) 可以嵌套在 (6, 2) 内,但不能嵌套在 (3, 4) 内。
    你的任务是选出尽量多的矩形,使得除了最后一个之外,每一个矩形都可以嵌套在下一个矩形内。

    Input

    第一行一个正整数 n (n <= 1000)。
    接下来 n 行每行两个正整数 a, b 表示矩形 i 的长和宽。

    Output

    第一行一个整数 k 表示符合条件的最多矩形数。
    第二行 k 个整数依次表示符合条件矩形的编号,要求字典序最小。

    Sample Input

    8
    14 9
    15 19
    18 12
    9 10
    19 17
    15 9
    2 13
    13 10

    Sample Output

    4
    4 8 3 2

    Http

    CJOJ:http://oj.changjun.com.cn/problem/detail/pid/1070

    Source

    动态规划,图论

    解决思路

    这道题可以转化成图论问题。若有正方形A可以嵌套在正方形B中,我们就连一条边A->B,呢么这道题就转化成为求DAG(为什么是DAG呢,因为一个矩形不可能经过若干次嵌套后嵌套在自己里面,所以一定不会有环)上从任意一点出发的最长路径

    我们令F[i]表示从i出发的最长路径,那么对于所有存在的路径i->j,一定有F[i]=max(F[j]+1)。由于初始起点不好计算(其实也可以计算,如用拓扑排序),为了方便期间,我们用记忆化搜索来实现。

    为什么不令F[i]表示到i的最长路径呢?这样做虽然没错,但是最终输出路径时的解不一定是字典序(除非还用一个数组记录路径),因为在这里我们是用的从动归转移方程倒推路径。

    ,我们知道F[i]一定是从其能连到的一个点(假设是j)得到的,那么我们从1到n枚举,看是否满足F[i]=F[j]+1。因为我们是从小到大枚举的,又是正序输出,所以可以保证字典序。

    代码

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    const int maxN=2000;
    const int inf=2147483647;
    
    int n;
    int A[maxN];
    int B[maxN];
    int G[maxN][maxN];//用邻接矩阵存边
    int F[maxN];//从i出发的最长路,F[i]为-1时表示还未访问
    
    int dfs(int x);
    void outp(int ans);
    
    int main()
    {
    	memset(F,-1,sizeof(F));
    	memset(G,-1,sizeof(G));
    	cin>>n;
    	for (int i=1;i<=n;i++)
    	{
    		cin>>A[i]>>B[i];
    		if (A[i]<B[i])
    			swap(A[i],B[i]);
    	}
    	for (int i=1;i<=n;i++)//判断两个矩形是否满足嵌套关系
    		for (int j=1;j<=n;j++)
    			if (((A[i]<A[j])&&(B[i]<B[j]))||((A[i]<B[j])&&(B[i]<A[j])))
    				G[i][j]=1;
    	for (int i=1;i<=n;i++)
    		if (F[i]==-1)
    			F[i]=dfs(i);
    	/*cout<<"FFF"<<endl;
    	for (int i=1;i<=n;i++)
    		cout<<F[i]<<' ';
    	cout<<endl;*/
    	int Ans=0;
    	for (int i=1;i<=n;i++)
    		if (F[i]>F[Ans])
    			Ans=i;//找出最大值
    	cout<<F[Ans]<<endl;
    	outp(Ans);//倒推出方案
    	cout<<endl;
    	return 0;
    }
    
    int dfs(int x)//记忆化搜索
    {
    	if (F[x]!=-1)//说明F[x]已经计算过了,直接返回
    		return F[x];
    	F[x]=1;
    	for (int i=1;i<=n;i++)
    		if (G[x][i]==1)//求出F[x]的最大值
    			F[x]=max(F[x],dfs(i)+1);
    	return F[x];
    }
    
    void outp(int ans)//倒推出解,但要注意是正序输出(因为我们定义的F[i]表示的是从i出发的最短路,所以推的时候是顺着最短路推的)
    {
    	cout<<ans<<' ';
    	for (int i=1;i<=n;i++)
    		if ((G[ans][i]==1)&&(F[ans]==F[i]+1))
    		{
    			outp(i);
    			return;
    		}
    	return;
    }
    
    自己选择的路,跪着也要走完。朋友们,虽然这个世界日益浮躁起来,只要能够为了当时纯粹的梦想和感动坚持努力下去,不管其它人怎么样,我们也能够保持自己的本色走下去。
  • 相关阅读:
    Python基础09 面向对象的进一步拓展
    Python快速教程 (手册)
    Python基础03 序列
    Python基础04 运算
    Python基础08 面向对象的基本概念
    Python基础07 函数
    Python基础10 反过头来看看
    Python基础05 缩进和选择
    Python进阶02 文本文件的输入输出
    Python进阶01 词典
  • 原文地址:https://www.cnblogs.com/SYCstudio/p/7138157.html
Copyright © 2020-2023  润新知