LOJ 2979 「THUSCH 2017」换桌——多路增广费用流
題目:https://loj.ac/problem/2979
原來的思路:
優(yōu)化連邊。一看就是同一個(gè)桌子相鄰座位之間連邊、相鄰桌子對應(yīng)座位之間連邊。
每個(gè)座位向它所屬的桌子連邊。然后每個(gè)人建一個(gè)點(diǎn),向若干桌子連邊。
因?yàn)檫B邊的桌子是區(qū)間,所以線段樹優(yōu)化。
又想到志愿者招募之類的,所以想弄一個(gè)上下界費(fèi)用流。人向它的座位連下界為1的邊,對應(yīng)桌子區(qū)間向人連邊。找一些循環(huán)流。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define ls Ls[cr] #define rs Rs[cr] using namespace std; int rdn() {int ret=0;bool fx=1;char ch=getchar();while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} const int N=305,M=15,N2=7000,M2=140000,INF=3005; int n,m,S,T,L[N][M],R[N][M],dy[N],bh[N][M],tot,Ls[N<<1],Rs[N<<1]; int rd[N2],hd[N2],xnt=1,to[M2],nxt[M2],cap[M2],w[M2]; int ans,dis[N2],pr[N2],info[N2]; bool ins[N2]; queue<int> q; void add(int x,int y,int l,int r,int z) {rd[y]+=l; rd[x]-=l; ans+=l*z; r-=l;to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=r;w[xnt]=z;to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z; } void build(int l,int r,int cr) {if(l==r){dy[l]=cr;return;}int mid=l+r>>1;ls=++tot; build(l,mid,ls);rs=++tot; build(mid+1,r,rs);add(ls,cr,0,INF,0); add(rs,cr,0,INF,0); } void qry(int l,int r,int cr,int L,int R,int k) {if(l>=L&&r<=R){printf("(%d->%d)\n",cr,k);add(cr,k,0,1,0);return;}int mid=l+r>>1;if(L<=mid)qry(l,mid,ls,L,R,k);if(mid<R)qry(mid+1,r,rs,L,R,k); } bool spfa() {memset(dis,0x3f,sizeof dis); info[T]=0;dis[S]=0; info[S]=INF; q.push(S); ins[S]=1;while(q.size()){int k=q.front(); q.pop(); ins[k]=0;for(int i=hd[k],v;i;i=nxt[i])if(cap[i]&&dis[v=to[i]]>dis[k]+w[i]){dis[v]=dis[k]+w[i];pr[v]=i; info[v]=Mn(info[k],cap[i]);if(!ins[v])ins[v]=1,q.push(v);}}return info[T]; } int ek() {int tp=info[T];for(int i=pr[T];i;i=pr[to[i^1]]){cap[i]-=tp; cap[i^1]+=tp;ans+=w[i]*tp;printf("%d ",to[i]);}puts("");return tp; } int main() {n=rdn();m=rdn();for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)L[i][j]=rdn()+1;//+1for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)R[i][j]=rdn()+1;tot=1;build(1,n,1); int tmp=n*m;for(int i=1;i<=n;i++,puts(""))for(int j=1;j<=m;j++)bh[i][j]=++tot,printf("%d ",tot);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){qry(1,n,1,L[i][j],R[i][j],bh[i][j]+tmp);printf("(%d->%d)\n",bh[i][j]+tmp,bh[i][j]);add(bh[i][j]+tmp,bh[i][j],1,1,0);printf("(%d->%d)\n",bh[i][j],dy[i]);add(bh[i][j],dy[i],1,1,0);printf("(%d->%d)[]\n",bh[i][j],bh[i][j==m?1:j+1]);add(bh[i][j],bh[i][j==m?1:j+1],0,INF,1);printf("(%d->%d)[]\n",bh[i][j],bh[i][j>1?j-1:m]);add(bh[i][j],bh[i][j>1?j-1:m],0,INF,1);if(i<n){printf("(%d->%d)[]\n",bh[i][j],bh[i+1][j]);add(bh[i][j],bh[i+1][j],0,INF,2);}}tot+=tmp; S=tot+1; T=tot+2; tmp=0;printf("S=%d T=%d\n",S,T);for(int i=1;i<=tot;i++)if(rd[i]<0)add(i,T,0,-rd[i],0);else if(rd[i]>0)add(S,i,0,rd[i],0),tmp+=rd[i];printf("tmp=%d ans=%d\n",tmp,ans);while(spfa())tmp-=ek();if(tmp)puts("no solution");else printf("%d\n",ans);return 0; } View Code然后發(fā)現(xiàn)過不了樣例。這樣是不行的。希望的是 “因?yàn)樽约哼B向 x 座位、 y 桌子向自己連邊,所以 x 座位到 y 桌子走了一條路” , 但實(shí)際上這樣相當(dāng)于自己給 x 座位一些流量、自己向 y 桌子索求一些流量;如果有另一個(gè)人,給 y 桌子某座位一些流量、向 x 座位所在桌子索求流量,那么 x 和 y 之間本應(yīng)有兩條路,現(xiàn)在一條也沒有了。
正解和這個(gè)類似?
每種座位都建兩棵線段樹,維護(hù)所有桌子,一棵表示向左走,一棵表示向右走;即一共 2*m 棵線段樹。
然后每個(gè)人向線段樹對應(yīng)節(jié)點(diǎn)連邊;線段樹葉子就表示座位;同一個(gè)桌子的座位之間連邊即可。
向左走的線段樹,從自己到左孩子連的邊,費(fèi)用需要加上 “跨過右孩子” 的代價(jià)。即到達(dá)向左走的線段樹某節(jié)點(diǎn),默認(rèn)當(dāng)前在該區(qū)間的右端點(diǎn)。所以每個(gè)人向區(qū)間連的 log 條邊要注意一下初始費(fèi)用。
需要多路增廣費(fèi)用流才能過。多路增廣,就是 spfa 一次之后,根據(jù) dis[ cr ] 和 dis[ v ] 的關(guān)系,像 dinic 一樣走。 dinic 的優(yōu)化都可以加(似乎一定要加當(dāng)前弧優(yōu)化?),注意要像 dfs 一樣打 vis 標(biāo)記,因?yàn)?dis 上可能有 0 環(huán)。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> #define ls Ls[cr] #define rs Rs[cr] using namespace std; int rdn() {int ret=0;bool fx=1;char ch=getchar();while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();}while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar();return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} int Mx(int a,int b){return a>b?a:b;} const int N=18005,M=276005,INF=3005; int n,m,S,T,L[305][15],R[305][15],bh[305][15]; int tot,Ls[N],Rs[N],hd[N],xnt=1,to[M],nxt[M],cap[M],w[M]; int ans,mxflow,dis[N],pr[N],info[N]; bool ins[N]; int cur[N];bool vis[N]; queue<int> q; void add(int x,int y,int c,int z) {to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=c;w[xnt]=z;to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z; } void build(int l,int r,int cr,bool fx,int k) {if(l==r){if(!fx)bh[l][k]=++tot;add(cr,bh[l][k],INF,0); return;}int mid=l+r>>1;ls=++tot; build(l,mid,ls,fx,k);rs=++tot; build(mid+1,r,rs,fx,k);if(fx){ add(cr,ls,INF,0); add(cr,rs,INF,(mid-l+1)*2);}else{ add(cr,ls,INF,(r-mid)*2); add(cr,rs,INF,0);} } void qry(int l,int r,int cr,int L,int R,int lj,bool fx) {if(l>=L&&r<=R){ add(tot,cr,1,lj);return;}int mid=l+r>>1;if(!fx){if(L<=mid)qry(l,mid,ls,L,R,lj+2*(r-mid),fx);if(mid<R)qry(mid+1,r,rs,L,R,lj,fx);}else{if(L<=mid)qry(l,mid,ls,L,R,lj,fx);if(mid<R)qry(mid+1,r,rs,L,R,lj+2*(mid-l+1),fx);} } bool spfa() {memset(dis,0x3f,sizeof dis); info[T]=0;dis[S]=0; info[S]=INF; q.push(S); ins[S]=1;while(q.size()){int k=q.front(); q.pop(); ins[k]=0;for(int i=hd[k],v;i;i=nxt[i])if(cap[i]&&dis[v=to[i]]>dis[k]+w[i]){dis[v]=dis[k]+w[i];pr[v]=i; info[v]=Mn(info[k],cap[i]);if(!ins[v])ins[v]=1,q.push(v);}}return info[T]; } int dfs(int cr,int flow) {if(cr==T)return flow;int use=0; vis[cr]=1;for(int &i=cur[cr],v;i;i=nxt[i])if(cap[i]&&!vis[v=to[i]]&&dis[v]==dis[cr]+w[i]){int tmp=dfs(v,Mn(flow-use,cap[i]));if(!tmp)dis[v]=-1;cap[i]-=tmp; cap[i^1]+=tmp; ans+=tmp*w[i];use+=tmp; if(use==flow){vis[cr]=0;return use;}}vis[cr]=0; return use; } void solve() {int tmp;while(spfa()){memcpy(cur,hd,sizeof hd);while(tmp=dfs(S,INF))mxflow+=tmp;} } int main() {n=rdn();m=rdn();for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) L[i][j]=rdn()+1;//+1for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) R[i][j]=rdn()+1;tot=2*m;for(int i=1;i<=m;i++){ build(1,n,i,0,i); build(1,n,i+m,1,i);}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ add(bh[i][j],bh[i][j==m?1:j+1],INF,1);add(bh[i][j],bh[i][j==1?m:j-1],INF,1);}for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){tot++;if(L[i][j]<i)qry(1,n,j,L[i][j],Mn(i-1,R[i][j]),-2*(n-i),0);if(R[i][j]>=i)qry(1,n,j+m,Mx(L[i][j],i),R[i][j],-2*(i-1),1);}S=tot+1; T=tot+2;for(int i=tot-n*m+1;i<=tot;i++)add(S,i,1,0);for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)add(bh[i][j],T,1,0);solve();if(mxflow<n*m)puts("no solution");else printf("%d\n",ans);return 0; }?
轉(zhuǎn)載于:https://www.cnblogs.com/Narh/p/10841141.html
總結(jié)
以上是生活随笔為你收集整理的LOJ 2979 「THUSCH 2017」换桌——多路增广费用流的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 局域网内网关欺骗获取网站密码
- 下一篇: 洛天依官方原版mmd模型_【MMD模型】