bzoj 2535 & bzoj 2109 航空管制 —— 贪心+拓扑序
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2535
https://www.lydsy.com/JudgeOnline/problem.php?id=2109
这个题,如果正着考虑,也就是先考虑放在前面的再考虑放在后面的,决策时会有矛盾;
也就是,如果要求 pos[a] < pos[b],则先考虑放 a,因为许多点放在 a 后面,所以 a 尽量往前放可以给它们留出空位;
但又有限制最晚起飞时间,那么先考虑的 a 应该尽量放在靠近它最晚起飞时间的地方,以免后面的点无法满足起飞时间的限制;
于是尽量往前放和尽量往后放形成了矛盾,无法决策;
所以需要转化一个限制,不妨对于 pos[a] < pos[b],先决策 b;
于是两个限制合起来的要求就都变成尽量往后放了(真神奇);
所以我们就这样倒着做,一定能得到合法的序列;
然后考虑每个点的可能最前位置,其实就是做的过程中把这个点以及它限制的点都暂时去掉,让其他点放好,那么不能再放的时候就意味着只有放上这个点才能继续,这时这个点按刚才做法找到的位置就是它的最前位置(其他点已经尽量把它的可行位置挤到前面了);
注意放点位置是最晚起飞时间和限制它的点的起飞时间取 min 的!
注意答案是起飞序列!而不是每个点的起飞位置!
如果用 set 找某位置前面第一个空位会很慢,还是并查集好。
代码如下:
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<set> using namespace std; int const xn=2005,xm=10005; int n,m,hd[xn],ct,to[xm],nxt[xm],deg[xn],tmp[xn],ans[xn],d[xn],mn[xn]; struct N{ int v,id; N(int v=0,int i=0):v(v),id(i) {} }; queue<int>q; set<int>st; set<int>::iterator it; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} void topo(int nw) { for(int i=1;i<=n;i++) { mn[i]=min(n,d[i]); tmp[i]=deg[i]; if(!tmp[i]&&i!=nw)q.push(i); } while(q.size()) { int x=q.front(); q.pop(); it=st.lower_bound(n-mn[x]+1); int tim=*it; //ans[x]=n-tim+1; //!!! ans[n-tim+1]=x; st.erase(tim); for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==nw)continue; tmp[u]--; mn[u]=min(mn[u],mn[x]);//!! if(!tmp[u])q.push(u); } } } int solve(int x) { st.clear(); for(int i=1;i<=n;i++)st.insert(i); topo(x); it=st.lower_bound(n-d[x]+1); return n-*it+1; } int main() { n=rd(); m=rd(); for(int i=1;i<=n;i++)d[i]=rd(),st.insert(i); for(int i=1,x,y;i<=m;i++)x=rd(),y=rd(),add(y,x),deg[x]++; topo(0); for(int i=1;i<=n;i++)printf("%d ",ans[i]); puts(""); for(int i=1;i<=n;i++)printf("%d ",solve(i)); puts(""); return 0; }
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; int const xn=2005,xm=10005; int n,m,hd[xn],ct,to[xm],nxt[xm],deg[xn],tmp[xn],ans[xn],d[xn],mn[xn]; int fa[xn],q[xn]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void add(int x,int y){to[++ct]=y; nxt[ct]=hd[x]; hd[x]=ct;} int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void topo(int nw) { int cnt=0; for(int i=1;i<=n;i++) { mn[i]=min(n,d[i]); tmp[i]=deg[i]; fa[i]=i; if(!tmp[i]&&i!=nw)q[++cnt]=i; } while(cnt) { int x=q[cnt--]; int tim=find(mn[x]); ans[tim]=x; fa[tim]=tim-1; for(int i=hd[x],u;i;i=nxt[i]) { if((u=to[i])==nw)continue; tmp[u]--; mn[u]=min(mn[u],mn[x]);//!! if(!tmp[u])q[++cnt]=u; } } } int solve(int x) { topo(x); return find(d[x]); } int main() { n=rd(); m=rd(); for(int i=1;i<=n;i++)d[i]=rd(); for(int i=1,x,y;i<=m;i++)x=rd(),y=rd(),add(y,x),deg[x]++; topo(0); for(int i=1;i<=n;i++)printf("%d ",ans[i]); puts(""); for(int i=1;i<=n;i++)printf("%d ",solve(i)); puts(""); return 0; }