uoj455 【UER #8】雪灾与外卖
题解:
https://blog.csdn.net/litble/article/details/88410435
https://www.mina.moe/archives/11762
以下是我瞎bb的:
如果我没看错的话,这个模拟费用流模拟的根本不是一般的最短增广路算法,模拟的是消圈算法...无语,为什么网上好像都直接当成对的了。另外,如果真的从费用流模型开始思考的话,好像会累死人而且完全没有结果;还是应该当做一个贪心来看待,费用流当做证明方法就行了
消圈算法就是先随便跑一个最大流,然后每次找出任意一个"残量网络(指只保留'原图中边和用于退流的反向边中剩余可用容量不为0的边'形成的图)中的负费用环",增广满,直到找不到负费用环,此时就是最小费用最大流(证明咕咕咕了)
这题里面如果遇到传送点时箱子堆是空的,就直接丢进传送点堆就行(代价自然为费用-位置);如果遇到箱子时传送点堆是空的,要假设左侧无限远处有无限个传送点(由于传送点不一定必须被匹配,但是箱子一定必须被匹配);同样的,遇到传送点时如果进行匹配不能让答案更小,就不要匹配,直接丢进传送点堆
这题餐厅可能可以多次匹配,方法是记一个pair,第二维是剩余容量,要拆的时候拆;直接这样搞复杂度是错的,但是向餐厅堆里面插入元素的次数是可以控制的(把多次一样的插入并成插入一次一个pair),这样复杂度就对了;复杂度证明网上有
最终代码:
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 using namespace std; 7 #define fi first 8 #define se second 9 #define mp make_pair 10 #define pb push_back 11 typedef long long ll; 12 typedef unsigned long long ull; 13 struct P 14 { 15 ll x,y; 16 }; 17 bool operator<(const P &a,const P &b) 18 { 19 return a.x>b.x; 20 } 21 struct PP 22 { 23 int x,w,c; 24 }p[200011]; 25 bool operator<(const PP &a,const PP &b) 26 { 27 return a.x<b.x; 28 } 29 int n,m; 30 ll ans; 31 priority_queue<P> q1,q2; 32 int main() 33 { 34 int i;P t;ll v,ts=0,t1,t2; 35 scanf("%d%d",&n,&m); 36 for(i=1;i<=n;++i) 37 { 38 scanf("%d",&p[i].x); 39 p[i].c=-1; 40 } 41 for(i=1;i<=m;++i) 42 { 43 scanf("%d%d%d",&p[i+n].x,&p[i+n].w,&p[i+n].c); 44 ts+=p[i+n].c; 45 } 46 if(n>ts) {puts("-1");return 0;} 47 sort(p+1,p+n+m+1); 48 for(i=1;i<=n+m;++i) 49 { 50 if(p[i].c==-1) 51 { 52 if(q2.empty()) v=1e11; 53 else 54 { 55 t=q2.top();q2.pop(); 56 v=t.x; 57 if(t.y!=1) q2.push((P){t.x,t.y-1}); 58 } 59 ans+=p[i].x+v; 60 q1.push((P){-2*p[i].x-v,1}); 61 } 62 else 63 { 64 t1=0; 65 while(p[i].c && !q1.empty()) 66 { 67 t=q1.top();v=t.x; 68 if(p[i].x+p[i].w+v>=0) break; 69 q1.pop(); 70 t2=min(ll(p[i].c),t.y); 71 t.y-=t2;p[i].c-=t2; 72 ans+=t2*(p[i].x+p[i].w+v); 73 t1+=t2; 74 q2.push((P){-2*p[i].x-v,t2}); 75 if(t.y) q1.push(t); 76 } 77 if(t1) q1.push((P){-p[i].x-p[i].w,t1}); 78 if(p[i].c) q2.push((P){p[i].w-p[i].x,p[i].c}); 79 } 80 } 81 printf("%lld\n",ans); 82 return 0; 83 }
暴力( 最暴力的费用流,$O(n^2)$条边):
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<queue> 6 #include<ext/pb_ds/assoc_container.hpp> 7 #include<ext/pb_ds/priority_queue.hpp> 8 using namespace std; 9 #define fi first 10 #define se second 11 #define pb push_back 12 typedef long long ll; 13 typedef unsigned long long ull; 14 struct pli 15 { 16 ll fi;int se; 17 }; 18 bool operator>(const pli &a,const pli &b) 19 { 20 return a.fi>b.fi || (a.fi==b.fi && a.se>b.se); 21 } 22 const ll inf=0x3f3f3f3f3f3f3f3f; 23 namespace F 24 { 25 26 struct E 27 { 28 int to,nxt,from; 29 ll d,cap,flow; 30 }e[1010000]; 31 int f1[5010],ne=1; 32 int S,T,n; 33 bool inq[5010],*vis=inq; 34 ll d[5010]; 35 ll h[5010]; 36 ll flow,cost; 37 typedef __gnu_pbds::priority_queue<pli,greater<pli> > pq; 38 pq::point_iterator it[5010]; 39 //const int INF=0x3f3f3f3f; 40 bool spfa() 41 { 42 int u,k,k1; 43 memset(d,0x3f,sizeof(d[0])*(n+1)); 44 memset(inq,0,sizeof(inq[0])*(n+1)); 45 queue<int> q; 46 q.push(T);d[T]=0;inq[T]=1; 47 while(!q.empty()) 48 { 49 u=q.front();q.pop(); 50 inq[u]=0; 51 for(k1=f1[u];k1;k1=e[k1].nxt) 52 { 53 k=k1^1; 54 if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from]) 55 { 56 d[e[k].from]=d[u]+e[k].d; 57 if(!inq[e[k].from]) 58 { 59 inq[e[k].from]=1; 60 q.push(e[k].from); 61 } 62 } 63 } 64 } 65 return d[S]!=inf; 66 } 67 bool dij() 68 { 69 int i,u,k,k1;pli t; 70 memset(d,0x3f,sizeof(d[0])*(n+1)); 71 memset(vis,0,sizeof(vis[0])*(n+1)); 72 pq q; 73 for(i=1;i<=n;i++) it[i]=q.end(); 74 it[T]=q.push((pli){0,T});d[T]=0; 75 while(!q.empty()) 76 { 77 t=q.top();q.pop(); 78 u=t.se; 79 if(vis[u]) continue; 80 vis[u]=1; 81 for(k1=f1[u];k1;k1=e[k1].nxt) 82 { 83 k=k1^1; 84 if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from]) 85 { 86 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from]; 87 if(it[e[k].from]!=q.end()) q.modify(it[e[k].from],(pli){d[e[k].from],e[k].from}); 88 else it[e[k].from]=q.push((pli){d[e[k].from],e[k].from}); 89 } 90 } 91 } 92 return d[S]!=inf; 93 } 94 void update() 95 { 96 for(int i=1;i<=n;i++) h[i]+=d[i]; 97 } 98 ll dfs(int u,ll x) 99 { 100 if(u==T||x==0) return x; 101 ll flow=0,f; 102 vis[u]=1; 103 for(int k=f1[u];k;k=e[k].nxt) 104 if(!vis[e[k].to]&&e[k].cap>e[k].flow&&h[e[k].to]==h[u]-e[k].d) 105 { 106 f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow)); 107 e[k].flow+=f;e[k^1].flow-=f;flow+=f; 108 if(flow==x) return flow; 109 } 110 return flow; 111 } 112 void augment() 113 { 114 ll f; 115 while(1) 116 { 117 memset(vis,0,sizeof(vis[0])*(n+1)); 118 f=dfs(S,inf); 119 if(!f) break; 120 flow+=f;cost+=f*h[S]; 121 } 122 } 123 void solve() 124 { 125 flow=cost=0; 126 memset(h,0,sizeof(h[0])*(n+1)); 127 if(!spfa()) return; 128 do 129 { 130 update(); 131 augment(); 132 }while(dij()); 133 } 134 void me(int a,int b,ll c,ll d) 135 { 136 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a; 137 e[ne].cap=c;e[ne].d=d; 138 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b; 139 e[ne].cap=0;e[ne].d=-d; 140 } 141 142 } 143 144 ll abs1(ll a){return a>=0?a:-a;} 145 int n,m; 146 int x[100011],y[100011],w[100011],c[100011]; 147 int main() 148 { 149 int i,j;ll ts=0; 150 scanf("%d%d",&n,&m); 151 for(i=1;i<=n;++i) 152 scanf("%d",x+i); 153 for(i=1;i<=m;++i) 154 { 155 scanf("%d%d%d",y+i,w+i,c+i); 156 ts+=c[i]; 157 } 158 if(n>ts) {puts("-1");return 0;} 159 F::S=n+m+1;F::T=n+m+2;F::n=n+m+2; 160 for(i=1;i<=n;++i) 161 F::me(F::S,i,1,0); 162 for(i=1;i<=n;++i) 163 for(j=1;j<=m;++j) 164 F::me(i,j+n,inf,abs1(x[i]-y[j])); 165 for(i=1;i<=m;++i) 166 F::me(i+n,F::T,c[i],w[i]); 167 F::solve(); 168 printf("%lld\n",F::cost); 169 return 0; 170 }
暴力(优化到O(n)条边):
1 %:pragma GCC optimize("Ofast") 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<vector> 6 #include<queue> 7 #include<ext/pb_ds/assoc_container.hpp> 8 #include<ext/pb_ds/priority_queue.hpp> 9 using namespace std; 10 #define fi first 11 #define se second 12 #define pb push_back 13 typedef long long ll; 14 typedef unsigned long long ull; 15 struct pli 16 { 17 ll fi;int se; 18 }; 19 bool operator>(const pli &a,const pli &b) 20 { 21 return a.fi>b.fi || (a.fi==b.fi && a.se>b.se); 22 } 23 const ll inf=0x3f3f3f3f3f3f3f3f; 24 namespace F 25 { 26 27 struct E 28 { 29 int to,nxt,from; 30 ll d,cap,flow; 31 }e[2010001]; 32 int f1[400011],ne=1; 33 int S,T,n; 34 bool inq[400011],*vis=inq; 35 ll d[400011]; 36 ll h[400011]; 37 ll flow,cost; 38 typedef __gnu_pbds::priority_queue<pli,greater<pli> > pq; 39 pq::point_iterator it[400011]; 40 //const int INF=0x3f3f3f3f; 41 bool spfa() 42 { 43 int u,k,k1; 44 memset(d,0x3f,sizeof(d[0])*(n+1)); 45 memset(inq,0,sizeof(inq[0])*(n+1)); 46 queue<int> q; 47 q.push(T);d[T]=0;inq[T]=1; 48 while(!q.empty()) 49 { 50 u=q.front();q.pop(); 51 inq[u]=0; 52 for(k1=f1[u];k1;k1=e[k1].nxt) 53 { 54 k=k1^1; 55 if(e[k].cap>e[k].flow&&d[u]+e[k].d<d[e[k].from]) 56 { 57 d[e[k].from]=d[u]+e[k].d; 58 if(!inq[e[k].from]) 59 { 60 inq[e[k].from]=1; 61 q.push(e[k].from); 62 } 63 } 64 } 65 } 66 return d[S]!=inf; 67 } 68 bool dij() 69 { 70 int i,u,k,k1;pli t; 71 memset(d,0x3f,sizeof(d[0])*(n+1)); 72 memset(vis,0,sizeof(vis[0])*(n+1)); 73 pq q; 74 for(i=1;i<=n;i++) it[i]=q.end(); 75 it[T]=q.push((pli){0,T});d[T]=0; 76 while(!q.empty()) 77 { 78 t=q.top();q.pop(); 79 u=t.se; 80 if(vis[u]) continue; 81 vis[u]=1; 82 for(k1=f1[u];k1;k1=e[k1].nxt) 83 { 84 k=k1^1; 85 if(e[k].cap>e[k].flow&&d[u]+e[k].d+h[u]-h[e[k].from]<d[e[k].from]) 86 { 87 d[e[k].from]=d[u]+e[k].d+h[u]-h[e[k].from]; 88 if(it[e[k].from]!=q.end()) q.modify(it[e[k].from],(pli){d[e[k].from],e[k].from}); 89 else it[e[k].from]=q.push((pli){d[e[k].from],e[k].from}); 90 } 91 } 92 } 93 return d[S]!=inf; 94 } 95 void update() 96 { 97 for(int i=1;i<=n;i++) h[i]+=d[i]; 98 } 99 ll dfs(int u,ll x) 100 { 101 if(u==T||x==0) return x; 102 ll flow=0,f; 103 vis[u]=1; 104 for(int k=f1[u];k;k=e[k].nxt) 105 if(!vis[e[k].to]&&e[k].cap>e[k].flow&&h[e[k].to]==h[u]-e[k].d) 106 { 107 f=dfs(e[k].to,min(x-flow,e[k].cap-e[k].flow)); 108 e[k].flow+=f;e[k^1].flow-=f;flow+=f; 109 if(flow==x) return flow; 110 } 111 return flow; 112 } 113 void augment() 114 { 115 ll f; 116 while(1) 117 { 118 memset(vis,0,sizeof(vis[0])*(n+1)); 119 f=dfs(S,inf); 120 if(!f) break; 121 flow+=f;cost+=f*h[S]; 122 } 123 } 124 void solve() 125 { 126 flow=cost=0; 127 memset(h,0,sizeof(h[0])*(n+1)); 128 if(!spfa()) return; 129 do 130 { 131 update(); 132 augment(); 133 }while(dij()); 134 } 135 void me(int a,int b,ll c,ll d) 136 { 137 e[++ne].to=b;e[ne].nxt=f1[a];f1[a]=ne;e[ne].from=a; 138 e[ne].cap=c;e[ne].d=d; 139 e[++ne].to=a;e[ne].nxt=f1[b];f1[b]=ne;e[ne].from=b; 140 e[ne].cap=0;e[ne].d=-d; 141 //printf("1t%d %d %lld %lld\n",a,b,c,d); 142 } 143 144 } 145 146 ll abs1(ll a){return a>=0?a:-a;} 147 int n,m; 148 int x[100011],y[100011],w[100011],c[100011]; 149 int main() 150 { 151 int i,j;ll ts=0; 152 scanf("%d%d",&n,&m); 153 for(i=1;i<=n;++i) 154 scanf("%d",x+i); 155 for(i=1;i<=m;++i) 156 { 157 scanf("%d%d%d",y+i,w+i,c+i); 158 ts+=c[i]; 159 } 160 if(n>ts) {puts("-1");return 0;} 161 F::S=n+3*m+1;F::T=n+3*m+2;F::n=n+3*m+2; 162 for(i=1;i<m;++i) 163 F::me(i,i+1,inf,y[i+1]-y[i]); 164 for(i=m;i>1;--i) 165 F::me(i+m,i-1+m,inf,y[i]-y[i-1]); 166 for(i=1;i<=m;++i) 167 { 168 F::me(i,i+2*m,inf,0); 169 F::me(i+m,i+2*m,inf,0); 170 F::me(i+2*m,F::T,c[i],w[i]); 171 } 172 for(i=1,j=1;i<=n;++i) 173 { 174 while(j<=m && y[j]<x[i]) ++j; 175 if(j!=1) F::me(i+3*m,j-1+m,1,x[i]-y[j-1]); 176 if(j!=m+1) F::me(i+3*m,j,1,y[j]-x[i]); 177 F::me(F::S,i+3*m,1,0); 178 } 179 F::solve(); 180 printf("%lld\n",F::cost); 181 return 0; 182 }
数据生成器
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 #include<chrono> 6 using namespace std; 7 #define fi first 8 #define se second 9 #define mp make_pair 10 #define pb push_back 11 typedef long long ll; 12 typedef unsigned long long ull; 13 ull splitmix64(ull x) { 14 x += 0x9e3779b97f4a7c15; 15 x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9; 16 x = (x ^ (x >> 27)) * 0x94d049bb133111eb; 17 return x ^ (x >> 31); 18 } 19 ull rd() 20 { 21 static ull x=splitmix64(chrono::steady_clock::now().time_since_epoch().count()); 22 return splitmix64(x=x*6364136223846793005ull+1442695040888963407ull); 23 } 24 ll randint(ll l,ll r) 25 { 26 return rd()%(r-l+1)+l; 27 } 28 int n=randint(2,5),m=randint(2,5); 29 struct P 30 { 31 int y,w,c; 32 }t2[100011]; 33 bool operator<(const P &a,const P &b) 34 { 35 return a.y<b.y; 36 } 37 int t1[100011]; 38 int main() 39 { 40 int i; 41 for(i=1;i<=n;++i) 42 t1[i]=randint(0,1e9); 43 for(i=1;i<=m;++i) 44 t2[i].y=randint(0,1e9),t2[i].w=randint(0,1e9),t2[i].c=randint(0,1e9); 45 sort(t1+1,t1+n+1); 46 sort(t2+1,t2+m+1); 47 printf("%d %d\n",n,m); 48 for(i=1;i<=n;++i) 49 printf("%d ",t1[i]); 50 puts(""); 51 for(i=1;i<=m;++i) 52 printf("%d %d %d\n",t2[i].y,t2[i].w,t2[i].c); 53 return 0; 54 }
对拍脚本
#!/bin/bash set -v on while [ 1 = 1 ] do ./1_2.out >t1.txt echo "running 1" ./1.out <t1.txt >t2.txt echo "running 2" ./1_1.out <t1.txt >t3.txt diff -w t2.txt t3.txt if [ $? != 0 ] then read -n 1 fi cat t2.txt done
优化暴力和最终代码拍了一下,应该是没问题了
一堆草稿...
令 𝑣𝑖为某种方式得到一个洞的权值,𝑤𝑖为某种方式得到一只兔子的权值,𝑥𝑖为兔子的坐标,𝑦𝑖为洞的坐标,𝑠𝑖为一个洞的附加权值。
一只兔子b如果选择洞a,总权值的增加量即为$w_a-y_a+x_b$。之后一个好洞c替换这个坏洞时,总权值又会增加$-(w_a-y_a)+y_c-2x_b$,对于好洞c来说,它找到了一只兔子,并且产生了$-(w_a-y_a)+y_c-2x_b$的贡献,因此我们可以新增加一只权值为$-(w_a-y_a)-x_b$的兔子。
一个洞 𝑏如果找到了兔子 𝑎,总权值的增加量 𝑤𝑎+𝑦𝑏+𝑠𝑏。之后一个兔子 𝑐抢走了这个洞时,总权值又会增加 𝑥𝑐−𝑤𝑎−2𝑦𝑏,对于 𝑐来说,它找到了一个洞,而 𝑎也不会因此无家可归,因为当我们处理 𝑎的时候,我们肯定为它分配了在它前面的洞,后来 𝑏找到了 𝑎,现在 𝑏把 𝑎赶走了,𝑎显然可以回去找之前分配给它的洞,赶走里面住的兔子,以此类推。这样,一切又回到了 𝑏没有发现 𝑎时的模样,因此,对于 𝑐来说,我们可以新增一个权值为 −𝑤𝑎−2𝑦𝑏的洞。
一个洞 𝑏如果找到了兔子 𝑎,总权值增加量 𝑤𝑎+𝑦𝑏+𝑠𝑏。之后一个新的洞 𝑐替代了 𝑏,总权值又会增加 𝑦𝑐+𝑠𝑐−𝑦𝑏−𝑠𝑏,对于 𝑐来说,它找到了一个权值为 −𝑦𝑏−𝑠𝑏的兔子,因此我们新增一个这样的兔子。
令$x_i$为兔子的坐标,$y_i$为洞的坐标,$s_i$为一个洞的附加权值。
一只兔子b如果选择洞a,总权值的增加量即为$s_a-y_a+x_b$。之后一个好洞c替换这个坏洞a时,总权值又会增加$-(s_a-y_a+x_b)+y_c+s_c-x_b=-s_a+y_a+y_c+s_c-2x_b$,对于好洞c来说,它找到了一只兔子,并且产生了$-s_a+y_a+y_c+s_c-2x_b$的贡献,因此我们可以新增加一只权值为$(-s_a+y_a+y_c+s_c-2x_b)-($的兔子。
一个洞 𝑏如果找到了兔子 𝑎,总权值的增加量 𝑤𝑎+𝑦𝑏+𝑠𝑏。之后一个兔子 𝑐抢走了这个洞时,总权值又会增加 𝑥𝑐−𝑤𝑎−2𝑦𝑏,对于 𝑐来说,它找到了一个洞,而 𝑎也不会因此无家可归,因为当我们处理 𝑎的时候,我们肯定为它分配了在它前面的洞,后来 𝑏找到了 𝑎,现在 𝑏把 𝑎赶走了,𝑎显然可以回去找之前分配给它的洞,赶走里面住的兔子,以此类推。这样,一切又回到了 𝑏没有发现 𝑎时的模样,因此,对于 𝑐来说,我们可以新增一个权值为 −𝑤𝑎−2𝑦𝑏的洞。
一个洞 𝑏如果找到了兔子 𝑎,总权值增加量 𝑤𝑎+𝑦𝑏+𝑠𝑏。之后一个新的洞 𝑐替代了 𝑏,总权值又会增加 𝑦𝑐+𝑠𝑐−𝑦𝑏−𝑠𝑏,对于 𝑐来说,它找到了一个权值为 −𝑦𝑏−𝑠𝑏的兔子,因此我们新增一个这样的兔子。
将兔子和洞从左到右排序,依次考虑。
匹配有两种:兔子与其之前的洞匹配(a类),洞与其之前的兔子匹配(b类)。
具体来说:
加入一个兔子,先与已经被考虑过的洞中剩余未被匹配的洞中最靠后的匹配(如果没有,就假设左侧无限远处有无限个的洞),可能要与之前的某个兔子交换洞。
遇到一个洞,可能会将之前某只兔子改为与其匹配。
设兔子的坐标为$x_i$,洞的坐标为$y_i$,洞的附加权值为$s_i$。
维护一个堆q1,存放"已经被考虑过的洞中剩余未被匹配的洞"的信息。
维护一个堆q2,存放"可能会在加入新洞的时候改变匹配的兔子"的信息。
一个兔子b选择洞a,总权值增加$s_a-y_a+x_b$。之后一个好洞c替换a时,总权值增加$-(s_a-y_a+x_b)+(y_c+s_c-x_b)=y_c+s_c-s_a+y_a-2x_b$。因此q2中放入一个$-s_a+y_a-2x_b$
我完全没看出来以下算法跟费用流有什么关系,也没看出来为什么是对的...
一个简化版的问题:如果每个洞容量都为1?
将兔子和洞从左到右排序,依次考虑。
匹配有两种:兔子与其之前的洞匹配(a类),洞与其之前的兔子匹配(b类)。
几种情况:
(废弃,可以发现无论如何都不可能更优)1.a类兔子A与之前的洞B匹配,可能洞会被A之后的兔子C抢走(指A与C交换它们所在的洞)("抢"前后都是a类)
1.a类兔子A与之前的洞B匹配,可能兔子会被A之后的洞C抢走,B就返回未被匹配时的状态("抢"前后由a类变成b类)
2.b类兔子A与之后的洞B匹配,可能洞会被B之后的兔子抢走,同时A回到前一次分配给它的洞(可能还会"递归"地将一些其他兔子赶回它们前一次所在的洞)("抢"前后由b类变成a类)
3.每遇到一个洞,都优先从
具体来说:
设兔子i的坐标为$x_i$,洞i的坐标为$y_i$,洞i的附加权值为$s_i$。
维护两个堆q1,q2,分别存放之前未使用的兔子/洞的"权值"。(刚插入堆的洞i的权值为$s_i-y_i$,兔子i的权值为$$)
遇到一个兔子b,先在q2中取出一个权值最小的洞(权值为a),与之匹配,产生贡献为$x_b+a$。如果有一个兔子d,原来匹配的洞权值是c(c,d间的匹配是a类),那么可能要改为d匹配洞a,b匹配洞c。可以发现修改前后贡献不变(都为$x_b+a+x_d+c$),因此在q2中放入一个权值为$$
问题:
数轴上有一些兔子和洞。一个兔子必须进一个洞,一个洞可以进一定容量的兔子(每个洞容量可能不同)。兔子可以向两边走,洞每进一个兔子需要一定额外费用(每个洞可能不同),最小化兔子行走的距离及选择的洞的额外费用之和。
我完全没看出来以下算法跟费用流有什么关系,也没看出来为什么是对的...
#猜了一下,大概是这样的意思:由于这个可以表示为费用流模型,且最大流是已知的(就是兔子数),如果任意得到一个方案,只需要不断做能使费用和变小的调整操作,最终一定能得到一种最优方案(这个结论真的对吗,我不会证)?
一个简化版的问题:如果每个洞容量都为1?
将兔子和洞从左到右排序,依次考虑。
设兔子i的坐标为$x_i$,洞i的坐标为$y_i$,洞i的附加权值为$s_i$。
维护两个堆q1,q2,分别存放之前未使用的兔子/洞的代价。(代价是人为定义的,新插入堆的洞i的权值为$s_i-y_i$,兔子i的权值为$$)
#匹配有两种:兔子与其之前的洞匹配(a类),洞与其之前的兔子匹配(b类)。
遇到一个兔子A,先把它与q2中代价最小(设代价为v)的洞匹配(如果没有了就假设左侧无限远处有无限个洞,这是由于所有兔子都必须匹配)。产生贡献$x_A+v$
可能有之后的兔子抢夺这个洞,。
可能有之后的洞(设为C)抢夺这个兔子,抢夺之后贡献变为$y_C-x_A+s_C$,增加了$y_C+s_C-2x_A-v$,因此向q1中放入一个权值为$-2x_A-v$的兔子来模拟撤销
遇到一个洞B,先把它与q1中代价最小(设代价为v)的兔子里匹配(如果没有了就直接放入q2,后面都不管)。
可能有之后的兔子抢夺这个洞,那么
下面有隐藏的备份...(尝试修过一些锅,不过可能越修越锅了也说不定?)