zoukankan      html  css  js  c++  java
  • bzoj 2427 软件安装

    题目描述

    现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi。我们希望从中选择一些软件安装到一台磁盘容量为M计算机上,使得这些软件的价值尽可能大(即Vi的和最大)。

    但是现在有个问题:软件之间存在依赖关系,即软件i只有在安装了软件j(包括软件j的直接或间接依赖)的情况下才能正确工作(软件i依赖软件j)。幸运的是,一个软件最多依赖另外一个软件。如果一个软件不能正常工作,那么它能够发挥的作用为0。

    我们现在知道了软件之间的依赖关系:软件i依赖软件Di。现在请你设计出一种方案,安装价值尽量大的软件。一个软件只能被安装一次,如果一个软件没有依赖则Di=0,这时只要这个软件安装了,它就能正常工作。

    输入

    第1行:N, M  (0<=N<=100, 0<=M<=500)
          第2行:W1, W2, ... Wi, ..., Wn (0<=Wi<=M )
          第3行:V1, V2, ..., Vi, ..., Vn  (0<=Vi<=1000 )
          第4行:D1, D2, ..., Di, ..., Dn(0<=Di<=N, Di≠i )

    输出

    一个整数,代表最大价值。

    样例输入

    3 10
    5 5 6
    2 3 4
    0 1 1

    样例输出

    5

    来源

    Day2

    (转自http://www.lydsy.com/JudgeOnline/problem.php?id=2427


       这道题看起来像是树归,首先来判断一下,一个软件只能依赖于一个软件,正如树,一个节点只有一个父节点(除了根节点)(把所有节点都连接到0号节点下),但是来构思一组数据:

      D: 2 3 1 

      像这样,1依赖于2,2依赖于3.,3依赖于1,构成了一个环(强连通分量),明显不符合树的性质,但是仔细想想,要么全选要么全不选

    就可以把它当成一个节点来看待。

      至于缩点。。。这个也比较简单,和codevs上"爱在心中"差不多,首先给belong数组赋初值:

    for(int i = 0;i <= n;i++)
    belong[i] = i;

      再Tarjan一次,将元素弹出栈时要将对应belong数组中这个强连通分量的值全部设成这中间任意元素的值(但必须一样)

    接着for循环扫描一次,凡是belong[i] != i的像这样处理一下:

    w[belong[i]] += w[i];
    v[belong[i]] += v[i];

      如果出现访问d[i]就像这样访问:

    d[belong[i]]......

      是不是十分方便快捷?(除了Tarjan算法的代码复杂度)

      下面思考一下树归方程,感觉如果玩多叉树的话状态很多,就转成二叉树,为了代码简洁,有这么两种方法

    第一种方法是记录每个节点添加进的"兄弟"的地址,就加一个指针变量,添加一个"儿子"或者"兄弟"时,就访问

    这个对应的指针变量,先正常加入,然后把指针指向它

    for(int i = 1;i <= n;i++){
        for(int j = head[i];j;j = edge[j].next){
            if(belong[i] == i && belong[i] != belong[edge[j].end]){
                if(node[i]->left != NULL)
                    node[i]->left->bro = &node[edge[belong[j]].end];
                else node[i]->left = &node[edge[belong[j]].end];;
            }
        }
    }

      是不是显得有点麻烦?而且严重牺牲了可读性,在看了某大神的代码后,我知道了这种方法:

    用bro[i]储存i节点的右子树(原先的"兄弟"),用son[i]储存i节点的左子树(原先的"子节点"),

    然后:

    最后:

    代码实现也比较简单:

    for(int i = 1;i <= n;i++){
        for(int j = head[i];j;j = edge[j].next){
            if(belong[i] == i && belong[i] != belong[edge[j].end]){
                bro[i] = son[belong[edge[j].end]];
                son[belong[edge[j].end]] = i;
            }
        }
    }

      另外关于左右子树出现空的时候,如果多加几个if语句的话,可能树形DP的代码就和Tarjan算法有的一拼了,可以把

    它的左右子树的位置设成一个值为0的节点,这就相当于一个空节点,但并不影响计算结果

      写出树归方程:

    f[index][i] = max(f[index][i],v[index] + f[left][j] + f[right][limit - j]);

    (index是第index个节点,limit是i - w[i],left = son[index],right = bro[index])

    最后附上我超级不简洁的代码(如果想要速度更快,可以把cin,cout改成scanf和printf)

    Code:

      1 /**
      2  * bzoj
      3  * Problem#2427
      4  * Accepted
      5  * Time:220ms
      6  * Memory:1492k
      7  */
      8 #include<iostream>
      9 #include<cstdio>
     10 #include<cstring>
     11 #include<stack>
     12 #include<vector>
     13 #define _min(a,b) ((a)<(b))?(a):(b)
     14 using namespace std;
     15 typedef bool boolean;
     16 typedef class Edge{
     17     public:
     18         int end;
     19         int next;
     20         Edge():end(0),next(0){}
     21         Edge(int end,int next):end(end),next(next){}
     22 }Edge;
     23 Edge *edge;
     24 int n,m;
     25 int *w;        //软件大小
     26 int *v;        //价值 
     27 int *d;
     28 int *head;
     29 int top;
     30 inline void addEdge(int from,int end){
     31     top++;
     32     edge[top].next=head[from];
     33     edge[top].end=end;
     34     head[from]=top;
     35 }
     36 boolean *visited;
     37 int *visitID;
     38 int *exitID;
     39 int entryed;
     40 stack<int> sta;
     41 int *belong;
     42 boolean *inStack;
     43 int *bro;
     44 int *son;
     45 void getSonMap(int end){
     46     int now=-1;
     47     int exits=0;
     48     while(now!=end){
     49         now=sta.top();
     50         belong[now]=end;
     51         inStack[now]=false;
     52         exits++;
     53         sta.pop();
     54     }
     55 }
     56 void Tarjan(const int pi){
     57     int index=head[pi];
     58     visitID[pi]=++entryed;
     59     exitID[pi]=visitID[pi];
     60     visited[pi]=true;
     61     inStack[pi]=true;
     62     sta.push(pi);
     63     while(index!=0){
     64         if(!visited[edge[index].end]){
     65             Tarjan(edge[index].end);
     66             exitID[pi]=_min(exitID[pi],exitID[edge[index].end]);
     67         }else if(inStack[edge[index].end]){
     68             exitID[pi]=_min(exitID[pi],visitID[edge[index].end]);
     69         }
     70         index=edge[index].next;
     71     }
     72     if(exitID[pi]==visitID[pi]){
     73         getSonMap(pi);
     74     }
     75 }
     76 void rebuild(){
     77     vector<int> indexs;
     78     for(int i=1;i<=n;i++){
     79         if(belong[i] != i){
     80             v[belong[i]] += v[i];
     81             w[belong[i]] += w[i];
     82             indexs.push_back(belong[i]);
     83         }
     84     }
     85     for(int i = 0;i < indexs.size();i++)
     86         edge[head[indexs[i]]].end = 0;
     87 }
     88 void create(){
     89     bro = new int[(const int)(n + 1)];
     90     son = new int[(const int)(n + 1)];
     91     for(int i = 0;i <= n;i++){
     92         son[i] = n + 1;
     93         bro[i] = n + 1;
     94     }
     95     for(int i = 1;i <= n;i++){
     96         for(int j = head[i];j;j = edge[j].next){
     97             if(belong[i] == i && belong[i] != belong[edge[j].end]){
     98                 bro[i] = son[belong[edge[j].end]];
     99                 son[belong[edge[j].end]] = i;
    100             }
    101         }
    102     }
    103 }
    104 int f[102][501];
    105 void solve(int index){
    106     if(index > n) return ;
    107     solve(son[index]);
    108     solve(bro[index]);
    109     int left = son[index];
    110     int right = bro[index];
    111     for(int i = 1;i <= m;i++){
    112         f[index][i] = max(f[index][i],f[right][i]);
    113         int limit = i - w[index];
    114         for(int j = 0;j <= limit;j++){
    115             f[index][i] = max(f[index][i],v[index] + f[left][j] + f[right][limit - j]);
    116         }
    117     }
    118 } 
    119 int main(){
    120     cin>>n>>m;
    121     head=new int[(const int)(n+1)];
    122     edge=new Edge[(const int)(n+1)];
    123     visited=new boolean[(const int)(n+1)];
    124     visitID=new int[(const int)(n+1)];
    125     exitID =new int[(const int)(n+1)];
    126     belong =new int[(const int)(n+1)];
    127     inStack=new boolean[(const int)(n+1)];
    128     memset(head,0,sizeof(int)*(n+1));
    129     memset(visited,false,sizeof(boolean)*(n+1));
    130     memset(inStack,false,sizeof(boolean)*(n+1));
    131     w = new int[(const int)(n + 1)];
    132     v = new int[(const int)(n + 1)];
    133     d = new int[(const int)(n + 1)];
    134     for(int i=1;i<=n;i++)
    135         scanf("%d",&w[i]);
    136     for(int i = 1;i <= n;i++)
    137         scanf("%d",&v[i]);
    138     for(int i = 1;i <= n;i++){
    139         scanf("%d",&d[i]);
    140         addEdge(i, d[i]);
    141     }
    142     for(int i=0;i<=n;i++) belong[i]=i;
    143     for(int i=1;i<=n;i++){
    144         if(!visited[i])
    145             Tarjan(i);
    146     }
    147     delete[] visited;
    148     delete[] inStack;
    149     rebuild();
    150     delete[] exitID;
    151     delete[] visitID; 
    152     create();
    153     w[0] = 0;
    154     v[0] = 0;
    155     solve(0);
    156     printf("%d",f[0][m]);
    157     return 0;
    158 }
  • 相关阅读:
    EasyUI基础searchbox&amp;progressbar(搜索框,进度条)
    git 仓库
    “农民代码”讨论
    多线程和多进程之间的区别
    move_uploaded_file
    在form里面,放了四个UEditor,怎么在后台分别获取它们值
    ThinkPHP模板IF标签用法详解
    Tp框架查询分页显示与全部查询出来显示运行时间快慢有区别吗?
    百度UEditor基本使用
    织梦DedeCms获取当前页面URL地址的调用方法
  • 原文地址:https://www.cnblogs.com/yyf0309/p/5679536.html
Copyright © 2011-2022 走看看