【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 }