【bzoj 2163】复杂的大门(算法效率--拆点+贪心)
阅读原文时间:2023年07月08日阅读:2

题目:你去找某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] 既满足 lrr,而且没有 l,r 都相同的传送梦。那么,我们把传送门按先起始点,再终结点从小到大的顺序排序之后,直接从前到后扫传送门,贪心每种用到极致 (•́⌄•́๑)૭✧,也就是在起始点和终结点都<= Fi 的情况下用到最多。这样可以的原因是:对于当前传送门 [l,r] 和下一个传送门 [ll,rr],若没有相等的,那么对于当前的 l 和 r 都是能减少花费就减少,因为没有其他的传送门能到达它们了。若是 l=ll,那么对于 r 就是尽量能减少花费就减少;而若是 r=rr,那就是对于 l 这样了。因此可以这样贪心。

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

1 #include
2 #include
3 #include
4 #include
5 #include
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.x1,所以不删次优的边
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 }