uoj455 【UER #8】雪灾与外卖

http://uoj.ac/problem/455

题解:

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 }
View Code

暴力( 最暴力的费用流,$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 }
View Code

暴力(优化到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 }
View Code

数据生成器

 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 }
View Code

对拍脚本

#!/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
View Code

优化暴力和最终代码拍了一下,应该是没问题了


一堆草稿...

令 𝑣𝑖为某种方式得到一个洞的权值,𝑤𝑖为某种方式得到一只兔子的权值,𝑥𝑖为兔子的坐标,𝑦𝑖为洞的坐标,𝑠𝑖为一个洞的附加权值。
一只兔子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,后面都不管)。
可能有之后的兔子抢夺这个洞,那么


下面有隐藏的备份...(尝试修过一些锅,不过可能越修越锅了也说不定?)


下面有隐藏的备份...

posted @ 2019-04-16 11:54  hehe_54321  阅读(658)  评论(0编辑  收藏  举报
AmazingCounters.com