[NOI2015]小园丁与老司机(DP+上下界最小流)

由于每行点的个数不超过1000,所以行内DP可以使用$O(n^2)$算法。

先找到每个点所能直接到达的所有点(x,y,x+y或x-y相同),用排序实现。

第一问:以行为阶段,对于每行,暴力枚举最有路径在这行上的起点和终点,g[x]记录当这行的最优路径以x为起点时,终点应在什么位置,f[x]记录走到x且这一行以x为起点,之后最多还能走到多少个点。

第二问:由于当一行的起点和终点都确定后,决策也是确定的,故只需要沿着DFS一遍即可得到最优路径。

第三问:先考虑怎么找到所有可能在最优路径上的边,同样暴力枚举每行的起点和终点即可,注意处理(0,0)的情况。找到符合要求的边后,问题就是用最少的路径数覆盖这些边,使用有上下界的最小流求解。

  1 #include<map>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
  5 #define For(i,x) for (int i=h[x],k; i; i=nxt[i])
  6 using namespace std;
  7 
  8 const int N=50010,M=1000010,inf=1000000000;
  9 bool va[N],vb[N];
 10 int n,mx,cnt=1,s0,ss,tt,S,T,d[N],f[N],h[N],dis[N],idx[N],q[N],cur[N],a[N],mp[N][6];
 11 int Lst[N],Nxt[N],pre[N],suf[N],premx[N],sufmx[N],to[M],fl[M],nxt[M],Do[N],Do2[N];
 12 struct P{ int x,y; }p[N];
 13 
 14 bool cmp1(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y<p[b].y; }
 15 bool cmp2(int a,int b){ return (p[a].x==p[b].x) ? p[a].y<p[b].y : p[a].x<p[b].x; }
 16 bool cmp3(int a,int b){ return (p[a].x-p[a].y==p[b].x-p[b].y) ? p[a].y<p[b].y : p[a].x-p[a].y<p[b].x-p[b].y; }
 17 bool cmp4(int a,int b){ return (p[a].x+p[a].y==p[b].x+p[b].y) ? p[a].y<p[b].y : p[a].x+p[a].y<p[b].x+p[b].y; }
 18 bool cmp5(int a,int b){ return (p[a].y==p[b].y) ? p[a].x<p[b].x : p[a].y>p[b].y; }
 19 
 20 inline void add(int u,int v,int w){
 21     to[++cnt]=v; fl[cnt]=w; nxt[cnt]=h[u]; h[u]=cnt;
 22     to[++cnt]=u; fl[cnt]=0; nxt[cnt]=h[v]; h[v]=cnt;
 23 }
 24 
 25 void work(int u,int v,int low,int high){ add(u,v,high-low); d[u]-=low; d[v]+=low; }
 26 
 27 bool bfs(){
 28     rep(i,0,n+6) dis[i]=0; dis[S]=1; q[1]=S;
 29     for (int st=0,ed=1; st<ed; ){
 30         int x=q[++st];
 31         For(i,x) if (fl[i] && !dis[k=to[i]]) dis[k]=dis[x]+1,q[++ed]=k;
 32     }
 33     return dis[T];
 34 }
 35 
 36 int dfs(int x,int lim){
 37     if (x==T) return lim;
 38     int c=0;
 39     for (int i=cur[x],k; i; i=nxt[i])
 40         if (fl[i] && dis[k=to[i]]==dis[x]+1){
 41             int t=dfs(k,min(lim-c,fl[i]));
 42             c+=t; fl[i]-=t; fl[i^1]+=t;
 43             if (fl[i]) cur[x]=i;
 44             if (c==lim) return lim;
 45         }
 46     if (!c) dis[x]=-1;
 47     return c;
 48 }
 49 
 50 int dinic(){
 51     int res=0;
 52     while (bfs()){
 53         rep(i,1,n+6) cur[i]=h[i];
 54         res+=dfs(S,inf);
 55     }
 56     return res;
 57 }
 58 
 59 void Print(int x){
 60     if (!x) return;
 61     printf("%d ",x);
 62     if (Do[x]==0) { Print(mp[x][Do2[x]]); return; }
 63     if (idx[x]>idx[Do[x]]){
 64         int t=Do[x];
 65         for (int i=Nxt[x]; i; i=Nxt[i]) printf("%d ",i);
 66         for (x=Lst[x]; x!=t; x=Lst[x]) printf("%d ",x);
 67         printf("%d ",t); Print(mp[t][Do2[t]]); return;
 68     }
 69     if (idx[x]<idx[Do[x]]){
 70         int t=Do[x];
 71         for (int i=Lst[x]; i; i=Lst[i]) printf("%d ",i);
 72         for (x=Nxt[x]; x!=t; x=Nxt[x]) printf("%d ",x);
 73         printf("%d ",t); Print(mp[t][Do2[t]]); return;
 74     }
 75 }
 76 
 77 void solve1(){
 78     rep(i,1,n) a[i]=i;
 79     sort(a+1,a+n+1,cmp1);
 80     rep(i,2,n) if (p[a[i-1]].y==p[a[i]].y) Lst[a[i]]=a[i-1],Nxt[a[i-1]]=a[i];
 81     sort(a+1,a+n+1,cmp2);
 82     rep(i,2,n) if (p[a[i-1]].x==p[a[i]].x) mp[a[i-1]][1]=a[i];
 83     sort(a+1,a+n+1,cmp3);
 84     rep(i,2,n) if (p[a[i-1]].x-p[a[i-1]].y==p[a[i]].x-p[a[i]].y) mp[a[i-1]][2]=a[i];
 85     sort(a+1,a+n+1,cmp4);
 86     rep(i,2,n) if (p[a[i-1]].x+p[a[i-1]].y==p[a[i]].x+p[a[i]].y) mp[a[i-1]][3]=a[i];
 87     sort(a+1,a+n+1,cmp5);
 88     rep(i,1,n) idx[a[i]]=i;
 89 }
 90 
 91 void W(int x){
 92     rep(i,1,3) if (mp[x][i]){
 93         int to=mp[x][i];
 94         if (f[to]+1>f[x]) Do2[x]=i,f[x]=f[to]+1;
 95     }
 96 }
 97 
 98 int calc(int x,int y){
 99     return (idx[x]<idx[y]) ? pre[y]+f[mp[y][Do2[y]]] : suf[y]+f[mp[y][Do2[y]]];
100 }
101 
102 bool jud(P &p){ return p.x==0 || p.x==p.y || p.x==-p.y; }
103 
104 void solve2(){
105     rep(k,1,n) if (p[a[k]].y!=p[a[k-1]].y) pre[a[k]]=1; else pre[a[k]]=pre[a[k-1]]+1;
106     for (int k=n; k; k--) if (p[a[k]].y!=p[a[k+1]].y) suf[a[k]]=1; else suf[a[k]]=suf[a[k+1]]+1;
107     rep(k,1,n) if (p[a[k]].y!=p[a[k-1]].y){
108         for (int x=a[k]; x; x=Nxt[x]) f[x]=1,W(x);
109         for (int x=a[k]; x; x=Nxt[x])
110             for (int y=a[k]; y; y=Nxt[y])
111                 if (y!=x && calc(x,y)>f[x]) f[x]=calc(x,y),Do[x]=y;
112     }
113     rep(i,1,n) if (jud(p[i])) mx=max(mx,f[i]);
114     printf("%d\n",mx);
115     rep(i,1,n) if (f[i]==mx && jud(p[i])) { Print(i); break; }
116     puts("");
117 }
118 
119 int solve3(){
120     rep(i,1,n) if (f[i]==mx && jud(p[i])) work(s0,i,1,inf),va[i]=1;
121     for (int i=n; i; i--) if (p[a[i]].y!=p[a[i+1]].y){
122         for (int x=a[i]; x; x=Lst[x]) if (va[x]){
123             if (f[mp[x][Do2[x]]]+1==f[x]) vb[x]=1;
124             for (int y=a[i]; y; y=Lst[y]) if (y!=x && calc(x,y)==f[x]) vb[y]=1;
125         }
126         for (int x=a[i]; x; x=Lst[x])
127             if (vb[x]) rep(j,1,3) if (mp[x][j] && f[mp[x][j]]==f[mp[x][Do2[x]]])
128                 va[mp[x][j]]=1,work(x,mp[x][j],1,inf);
129     }
130     rep(i,1,n+1) work(ss,i,0,inf),work(i,tt,0,inf);
131     rep(i,1,n+1) if (d[i]>0) add(S,i,d[i]); else add(i,T,-d[i]);
132     add(tt,ss,inf); dinic(); S=tt; T=ss; return inf-dinic();
133 }
134 
135 int main(){
136     freopen("driver.in","r",stdin);
137     freopen("driver.out","w",stdout);
138     scanf("%d",&n); s0=n+1; ss=n+2; tt=n+3; S=n+4; T=n+5;
139     rep(i,1,n) scanf("%d%d",&p[i].x,&p[i].y);
140     solve1(); solve2(); printf("%d\n",solve3());
141     return 0;
142 }

 

转载于:https://www.cnblogs.com/HocRiser/p/9436517.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值