zoukankan      html  css  js  c++  java
  • 【HDU4966】GGS-DDU

    题意

        有n种科目,每个科目都有一个最高的等级a[i]。开始的时候,每个科目的等级都是0。现在要选择一些课程进行学习使得每一个科目都达到最高等级。这里有m节课可供选择。对于每门课给出L1[i],c[i],L2[i],d[i],money[i],要选择这门课要求科目c[i]的等级不小于L[i],可以使科目d[i]的等级升为L2[i],花费金钱money[i]。请计算最小花费是多少。

    数据范围N<=50,M<=2000,sum of a[1] to a[n]不超过500.

    分析

      最小树形图,要用到朱刘算法。其实。。。我是昨晚在补这道题的时候才去学了一下朱刘算法。。。。。。

      朱刘算法学习博客https://blog.csdn.net/txl199106/article/details/62045479

      对于这道题我是这么想的,把每个科目的每个等级都拆成点,然后建一个编号为0的点向各个科目等级为0的点连一条权值为0的边。对于每个科目,将他们等级从高到低的点依次连权值为0的点,然后对于每一门课程按照要求连边,权值为这门课程的花费。

      建图完成以后,以0点为root跑朱刘算法得到最小树形图,然后这就是答案了。

      应该会这个算法就很好做的一道题。

      

      1 #include <cstdio>
      2 #include <iostream>
      3 #include <algorithm>
      4 #include <cstring>
      5 #include <vector>
      6 using namespace std;
      7 const int maxn=100000+100;
      8 const int INF=2147480000;
      9 
     10 struct Edge{
     11     int from,to;
     12     long long w;
     13 }edges[maxn];
     14 int sz;
     15 int a[100],sum[100];
     16 int N,M;
     17 void add_edge(int from,int to,long long w){
     18     ++sz;
     19     edges[sz].from=from;edges[sz].to=to;edges[sz].w=w;
     20 }
     21 int pre[maxn];//存储父节点
     22 int vis[maxn];//标记作用
     23 int id[maxn];//id[i]记录节点i所在环的编号
     24 int in[maxn];//in[i]记录i入边中最小的权值
     25 long long zhuliu(int root, int n, int m, Edge *edge)//root根 n点数 m边数
     26 {
     27     long long res = 0;
     28     int u, v;
     29     while(1)
     30     {
     31         for(int i = 0; i < n; i++)
     32             in[i] = INF;//初始化
     33         for(int i = 1; i <= m; i++)
     34         {
     35             Edge E = edge[i];
     36             if(E.from != E.to && E.w < in[E.to])
     37             {
     38                 pre[E.to] = E.from;//记录前驱
     39                 in[E.to] = E.w;//更新
     40             }
     41         }
     42         for(int i = 0; i <n; i++)
     43             if(i != root && in[i] == INF)
     44                 return -1;//有其他孤立点 则不存在最小树形图
     45         //找有向环
     46         int tn = 0;//记录当前查找中 环的总数
     47         memset(id, -1, sizeof(id));
     48         memset(vis, -1, sizeof(vis));
     49         in[root] = 0;//
     50         for(int i = 0; i <n; i++)
     51         {
     52             res += in[i];//累加
     53             v = i;
     54             //找图中的有向环 三种情况会终止while循环
     55             //1,直到出现带有同样标记的点说明成环
     56             //2,节点已经属于其他环
     57             //3,遍历到根
     58             while(vis[v] != i && id[v] == -1 && v != root)
     59             {
     60                 vis[v] = i;//标记
     61                 v = pre[v];//一直向上找
     62             }
     63             //因为找到某节点属于其他环  或者 遍历到根  说明当前没有找到有向环
     64             if(v != root && id[v] == -1)//必须上述查找已经找到有向环
     65             {
     66                 for(int u = pre[v]; u != v; u = pre[u])
     67                     id[u] = tn;//记录节点所属的 环编号
     68                 id[v] = tn++;//记录节点所属的 环编号  环编号累加
     69             }
     70         }
     71         if(tn == 0) break;//不存在有向环
     72         //可能存在独立点
     73         for(int i = 0; i <n; i++)
     74             if(id[i] == -1)
     75                 id[i] = tn++;//环数累加
     76         //对有向环缩点  和SCC缩点很像吧
     77         for(int i = 1; i <= m; i++)
     78         {
     79             v = edge[i].to;
     80             edge[i].from = id[edge[i].from];
     81             edge[i].to = id[edge[i].to];
     82             //<u, v>有向边
     83             //两点不在同一个环 u到v的距离为 边权cost - in[v]
     84             if(edge[i].from != edge[i].to)
     85                 edge[i].w -= in[v];//更新边权值 继续下一条边的判定
     86         }
     87         n = tn;//以环总数为下次操作的点数 继续执行上述操作 直到没有环
     88         root = id[root];
     89     }
     90     return res;
     91 }
     92 
     93 int main(){
     94     while(scanf("%d%d",&N,&M)!=EOF&&N&&M){
     95         sz=0;
     96         sum[0]=0;
     97         for(int i=1;i<=N;i++){
     98             scanf("%d",&a[i]);
     99             sum[i]=sum[i-1]+a[i]+1;
    100         }
    101 
    102         int c,L1,d,L2;
    103         long long w;
    104         for(int i=1;i<=M;i++){
    105             scanf("%d%d%d%d%lld",&c,&L1,&d,&L2,&w);
    106            /* for(int j=L1;j<=a[c];j++){
    107                 add_edge(sum[c-1]+j+1,sum[d-1]+L2+1,w);
    108             }*/
    109             add_edge(sum[c-1]+L1+1,sum[d-1]+L2+1,w);
    110         }
    111         for(int i=1;i<=N;i++){
    112             add_edge(0,sum[i-1]+1,0);
    113         }
    114         for(int i=1;i<=N;i++){
    115             for(int j=0;j<a[i];j++){
    116                 add_edge(sum[i-1]+j+2,sum[i-1]+j+1,0);
    117             }
    118         }
    119         /*printf("%d
    ",sz);
    120         for(int i=1;i<=sz;i++){
    121             printf("%d %d %d
    ",edges[i].from,edges[i].to,edges[i].w);
    122         }*/
    123         //cout<<sum[n]<<endl;
    124         long long ans=zhuliu(0,sum[N]+1,sz,edges);
    125         printf("%lld
    ",ans);
    126     }
    127 return 0;
    128 }
    View Code
  • 相关阅读:
    冲刺阶段个人博客9
    冲刺阶段个人博客8
    梦断代码阅读笔记02
    我关于搜狗输入法的用户体验描述
    冲刺阶段个人博客07
    冲刺阶段个人博客06
    冲刺阶段个人博客05
    冲刺阶段个人博客04
    BZOJ 2006 超级钢琴(堆+主席树)
    BZOJ 1924 所驼门王的宝藏(强连通分量缩点+DAG最长链)
  • 原文地址:https://www.cnblogs.com/LQLlulu/p/9007562.html
Copyright © 2011-2022 走看看