zoukankan      html  css  js  c++  java
  • 【bzoj 2163】复杂的大门(算法效率--拆点+贪心)

    题目:你去找某bm玩,到了门口才发现要打开他家的大门不是一件容易的事……
    他家的大门外有n个站台,用1到n的正整数编号。你需要对每个站台访问一定次数以后大门才能开启。站台之间有m个单向的传送门,通过传送门到达另一个站台不需要花费任何代价。而如果不通过传送门,你就需要乘坐公共汽车,并花费1单位的钱。值得庆幸的是,任意两个站台之间都有公共汽车直达。
    现在给你每个站台必须访问的次数Fi,对于站台i,你必须恰好访问Fi次(不能超过)。
    我们用u、v、w三个参数描述一个传送门,表示从站台u到站台v有一个最多可以使用w次的传送门(不一定要使用w次)。值得注意的是,对于任意一对传送门(u1,v1)和(u2,v2),如果有u1<u2,则有v1≤v2;如果有v1<v2,则有u1≤u2;且u1=u2和v1=v2不同时成立。
    你可以从任意的站台开始,从任意的站台结束。出发去开始的站台需要花费1单位的钱。你需要求出打开大门最少需要花费多少单位的钱。

    解法:由于要最小花费,那免费的传送门肯定尽量多用。而又要求每个站台必须不多不少访问 Fi 次,我们可以把每个站台拆成分成 “入度和出度”计算,也就是“到达和出发的次数”。
           接着就是贪心的思想。由题意可知,不存在 [l,r] 和 [ll,rr] 既满足 l<ll,又满足 r>rr,而且没有 l,r 都相同的传送梦。那么,我们把传送门按先起始点,再终结点从小到大的顺序排序之后,直接从前到后扫传送门,贪心每种用到极致 (•́⌄•́๑)૭✧,也就是在起始点和终结点都<= Fi 的情况下用到最多。这样可以的原因是:对于当前传送门 [l,r] 和下一个传送门 [ll,rr],若没有相等的,那么对于当前的 l 和 r 都是能减少花费就减少,因为没有其他的传送门能到达它们了。若是 l=ll,那么对于 r 就是尽量能减少花费就减少;而若是 r=rr,那就是对于 l 这样了。因此可以这样贪心。

          P.S.而我下面屏蔽的代码就是没有理解“拆点”的意义。(⊙_⊙;)… 一定要拆“入和出”,否则会漏算或多算的。

     1 #include<cstdio>
     2 #include<cstdlib>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<iostream>
     6 using namespace std;
     7 #define N 10010
     8 #define M 100010
     9 #define W 50010
    10 
    11 int n,m;
    12 int gin[N],gout[N];
    13 struct node{int x,y,w;}a[M];
    14 
    15 bool cmp(node x,node y)
    16 {
    17     if (x.x!=y.x) return x.x<y.x;
    18     return x.y<y.y;
    19 }
    20 int mmin(int x,int y) {return x<y?x:y;}
    21 int main()
    22 {
    23     int i,j,ans=0;
    24     scanf("%d%d",&n,&m);
    25     for (i=1;i<=n;i++)
    26     {
    27       scanf("%d",&gin[i]);
    28       gout[i]=gin[i];
    29       ans+=gin[i];
    30     }
    31     for (i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].w);
    32     sort(a+1,a+1+m,cmp);
    33     j=1;
    34     for (i=1;i<=n;i++)
    35     {
    36       while (j<=m && a[j].x==i)
    37       {
    38         int x=i,y=a[j].y;
    39         int tmp=mmin(mmin(gout[x],gin[y]),a[i].w);
    40         gout[x]-=tmp,gin[y]-=tmp;//直到现在能入y和出x的次数
    41         ans-=tmp, j++;
    42       }
    43     }
    44     /*for (i=1;i<=m;i++)
    45     {
    46       int x=a[i].x,y=a[i].y;
    47       int tmp=mmin(mmin(gout[x],gin[y]),a[i].w);
    48       gout[x]-=tmp,gin[y]-=tmp;
    49       ans-=tmp;
    50     }*/
    51     /*j=1;
    52     for (i=1;i<=n;i++)
    53     {
    54       while (j<=m && a[j].x==i)
    55       {
    56         int x=i,y=a[j].y;
    57         //int tmp=mmin(mmin(h[x],h[y]),a[j].w);
    58         //h[x]-=tmp,h[y]-=tmp;//同一个点重复计算了
    59         ans-=tmp, j++;
    60       }
    61     }*/
    62     /*  int cnt=0,p=1;//x min & y min
    63       for (i=2;i<=m;i++)
    64           if (a[i].x!=a[i-1].x) a[++p]=a[i];//推广!因为h[i]>1,所以不删次优的边
    65       p=1;
    66       for (i=2;i<=m;i++)
    67         if (a[i].y!=a[i-1].y) a[++p]=a[i];*/
    68     printf("%d
    ",ans);
    69     return 0;
    70 }
  • 相关阅读:
    初学移动专题
    IE下a标签跳转失败
    c++中一个多态的实例
    字符串中是否有相同的字符
    求乘积最大的连续子序列
    跳跃游戏
    求一个非负整数的平方根 二分法
    罗马数 与 整数 相互转换
    二进制相加
    链表分割
  • 原文地址:https://www.cnblogs.com/konjak/p/6071136.html
Copyright © 2011-2022 走看看