zoukankan      html  css  js  c++  java
  • sss

    『选课 有树形依赖的背包问题』

    <更新提示>

    <第一次更新>


    <正文>

    选课(tyvj 1051)

    Description

    学校实行学分制。每门的必修课都有固定的学分,同时还必须获得相应的选修课程学分。学校开设了N(N < 300)门的选修课程,每个学生可选课程的数量M是给定的。学生选修了这M门课并考核通过就能获得相应的学分。

    在选修课程中,有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才能选修。例如《Frontpage》必须在选修了《Windows操作基础》之后才能选修。我们称《Windows操作基础》是《Frontpage》的先修课。每门课的直接先修课最多只有一门。两门课也可能存在相同的先修课。每门课都有一个课号,依次为1,2,3,…。
    你的任务是为自己确定一个选课方案,使得你能得到的学分最多,并且必须满足先修课优先的原则。假定课程之间不存在时间上的冲突。

    Input Format

    输入文件的第一行包括两个整数N、M(中间用一个空格隔开)其中1≤N≤300,1≤M≤N。

    以下N行每行代表一门课。课号依次为1,2,…,N。每行有两个数(用一个空格隔开),第一个数为这门课先修课的课号(若不存在先修课则该项为0),第二个数为这门课的学分。学分是不超过10的正整数。

    Output Format

    输出文件只有一个数,实际所选课程的学分总数。

    Sample Input

    7 4
    2 2
    0 1
    0 4
    2 1
    7 1
    7 6
    2 2
    

    Sample Output

    13
    

    解析

    由题意可得,课程与课程之间的先修关系构成了一棵树,在最优化问题中,我们通常是用树形(DP)来求解的。那么我们可以先尝试树形(DP)的方法:设(f[i][j])代表以(i)为根的子树中选了(j)门课程的最大学分数。显然,这个状态的转移在每颗子树中都可以选取若干门课程,那么,我们首先可以得出如下的状态转移方程:

    [f[i][j]=max_{sum^{|son(i)|}_{k=1}c_k=j-1}{sum^{|son(i)|}_{k=1}f[son_k][c_k]}+score[i] ]

    其中(|son(i)|)代表(i)的子节点个数,(son_k)代表其中某个子节点的编号,(c_k)则代表对于每一颗子树选取的课程门数,需满足:(sum_{k=1}^{|son(i)|}c_k=j-1)
    事实上,这是一个分组背包模型:(j-1)即为背包体积,每一个(son_k)即为一组物品,每组组物品分别有(j-1)个,每一个体积分别为(c_k),价值为(f[son_k][c_k]),而每组物品我们只能从中选取至多一个,使得总价值最大。
    那么对于每一颗子树,我们就可以用分组背包的方程来转移了。

    [f[i][j]=max{f[i][t-j]+f[son][j]} ]

    注意一个细节,没有先修课的课程,在输入中我们认为它的先修课是一个虚拟课程(0),那么在(i=0)时,背包的体积为(j),不需要为该门课程本身留下一个位置,所以我们可以将带有这门课程本身的状态额外的转移一次。

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    const int N=300+30,M=300+30;
    int n,m,f[N][M],score[N]; 
    vector < int >Link[N];
    inline void input(void)
    {
    	scanf("%d%d",&n,&m);
    	for(int i=1;i<=n;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		Link[u].push_back(i);
    		score[i]=v;
    	}
    }
    inline void dp(int root)
    {
    	f[root][0]=0;
    	for(int i=0;i<Link[root].size();i++)
    	{
    		int Son=Link[root][i];
    		dp(Son);
    		for(int v=m;v>0;v--)
    		{
    			for(int j=1;j<=v;j++)
    			{
    				f[root][v]=max(f[root][v],f[Son][j]+f[root][v-j]);
    			}
    		}
    	}
    	if(root!=0)
    		for(int v=m;v>0;v--)
    			f[root][v]=f[root][v-1]+score[root];
    }
    int main(void)
    {
    	freopen("test.in","r",stdin);
    	freopen("test.out","w",stdout);
    	input();
    	dp(0);
    	printf("%d
    ",f[0][m]);
    }
    
    
    

    <后记>

  • 相关阅读:
    114.114.114.114和8.8.8.8
    一台电脑双网卡同时上网
    eNSP模拟器
    路由器UPnP
    子网掩码
    网线水晶头制作
    AP (无线访问接入点(WirelessAccessPoint))
    筛选键
    注册表方法修改网络名称
    图片素材网址
  • 原文地址:https://www.cnblogs.com/Parsnip/p/10333684.html
Copyright © 2011-2022 走看看