【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\n",ans);
69     return 0;
70 }

 

posted @ 2016-11-16 19:16  konjac蒟蒻  阅读(519)  评论(0编辑  收藏  举报