bzoj 5120 [2017國家集訓隊測試]無限之環——網路流
阿新 • • 發佈:2018-12-21
題目:https://www.lydsy.com/JudgeOnline/problem.php?id=5120
旋轉的話相當於去掉一個插頭、新增一個插頭,所以在這兩個插頭之間連邊並帶上費用即可。
網格圖可以黑白染色,轉化為相鄰格子間插頭的匹配問題。
注意:
1.黑白染色不是移動一格就 fx = ! fx ;每換一行,開頭位置的顏色應該和上一行的開頭不一樣!不然有偶數列的話自己原來寫的那個染色就崩了;
2. L 形的判斷不是 d&(d>>1) 判斷是否有兩個相鄰的1,如果是第一個位置和最後一個位置是1的話(9)就判斷不出來了!可以判斷 d != 5 && d != 10 ;
3.對於無解的判斷,比較好的是判斷插頭個數的是不是流量的兩倍。
然後就能很慢地 A 了。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=8005,K=2005,M=K<<4,INF=N;//<<4:*6 for trans *2 for between int n,m,t,bh[K][K],hd[N],xnt=1,to[M],nxt[M],cap[M],w[M],bin[5View Code];//=1! int dis[N],pre[N],info[N],ans,val,flow; bool ins[N]; queue<int> q; 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){returna<b?a:b;} void upd(int &x){x>=4?x-=4:0;} void add(int x,int y,int z) { if(!x||y==t)val++;// to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=z; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z; } void init() { int bin[5]; bin[0]=1;for(int i=1;i<4;i++)bin[i]=bin[i-1]<<1; bool Fx=0,fx=0; for(int i=1;i<=n;i++,Fx=!Fx)/////Fx!!!!! for(int j=1,fx=Fx;j<=m;j++,fx=!fx) { int d=rdn(),ct=0; if(!fx){for(int k=0;k<4;k++)if(d&bin[k])ct++,add(0,k+bh[i][j],0);} else {for(int k=0;k<4;k++)if(d&bin[k])ct++,add(k+bh[i][j],t,0);} if(ct==1||ct==3)///ct==1:not a chain but 1,1,2 { int x;//caution for ct=1|3 the diff if(ct==1) {for(int k=0;k<4;k++)if(d&bin[k]){x=k;break;}} else {for(int k=0;k<4;k++)if(!(d&bin[k])){x=k;break;}} int y=x^2,cr=x+bh[i][j]; for(int k=0;k<4;k++) if(k!=x)(fx^(ct==3))?add(k+bh[i][j],cr,1+(k==y)):add(cr,k+bh[i][j],1+(k==y)); } // else if(ct==2&&(d&(d>>1)))//L else if(ct==2&&(d!=5&&d!=10))/////caution 9!!! { for(int k=0;k<4;k++) if(d&bin[k])fx?add((k^2)+bh[i][j],k+bh[i][j],1):add(k+bh[i][j],(k^2)+bh[i][j],1); } if(!fx) { if(i>1)add(bh[i][j],2+bh[i-1][j],0); if(j<m)add(1+bh[i][j],3+bh[i][j+1],0); if(i<n)add(2+bh[i][j],bh[i+1][j],0); if(j>1)add(3+bh[i][j],1+bh[i][j-1],0); } } } bool spfa() { memset(dis,0x3f,sizeof dis);dis[0]=0; info[0]=INF;info[t]=0; q.push(0);ins[0]=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]; pre[v]=i;info[v]=Mn(info[k],cap[i]); if(!ins[v])q.push(v),ins[v]=1; } } return info[t]; } void ek() { int s=info[t]; flow+=s; ans+=dis[t]*s;//dis[t]! for(int i=pre[t];i;i=pre[to[i^1]]) {cap[i]-=s;cap[i^1]+=s;} } int main() { n=rdn();m=rdn();t=1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++,t+=4)bh[i][j]=t; init(); if(val&1){puts("-1");return 0;}// while(spfa())ek(); printf("%d\n",flow<<1==val?ans:-1);/// return 0; }
還學習了多路增廣SPFA演算法。大概適用於邊權變化範圍很小的那種吧。
因為邊權相近,所以期望做一次SPFA之後能不僅更新出一條路,還能更多地更新,因為算出來的 dis[ ] 可能對很多條路都適用。
所以就在SPFA之後按照 dis[ ] 來 dfs ,找找所有合法的路徑。在這道題上真的能快好多。
注意 dfs 要打 vis 標記,走過的就不再走了。因為網路裡可能有環,這個又是按 dis 來走的,所以比如有很多0權邊的話,就可能死迴圈。dinic 沒有這個問題是因為它是按 dfn 來走的,dfn 弄出來的一定是 DAG 。
#include<cstdio> #include<cstring> #include<algorithm> #include<queue> using namespace std; const int N=8005,K=2005,M=K<<4,INF=N;//<<4:*6 for trans *2 for between int n,m,t,bh[K][K],hd[N],xnt=1,cur[N],to[M],nxt[M],cap[M],w[M],bin[5];//=1! int dis[N],pre[N],info[N],ans,val,flow; bool ins[N]; queue<int> q; 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;} void upd(int &x){x>=4?x-=4:0;} void add(int x,int y,int z) { if(!x||y==t)val++;// to[++xnt]=y;nxt[xnt]=hd[x];hd[x]=xnt;cap[xnt]=1;w[xnt]=z; to[++xnt]=x;nxt[xnt]=hd[y];hd[y]=xnt;cap[xnt]=0;w[xnt]=-z; } void init() { int bin[5]; bin[0]=1;for(int i=1;i<4;i++)bin[i]=bin[i-1]<<1; bool Fx=0,fx=0; for(int i=1;i<=n;i++,Fx=!Fx)/////Fx!!!!! for(int j=1,fx=Fx;j<=m;j++,fx=!fx) { int d=rdn(),ct=0; if(!fx){for(int k=0;k<4;k++)if(d&bin[k])ct++,add(0,k+bh[i][j],0);} else {for(int k=0;k<4;k++)if(d&bin[k])ct++,add(k+bh[i][j],t,0);} if(ct==1||ct==3)///ct==1:not a chain but 1,1,2 { int x;//caution for ct=1|3 the diff if(ct==1) {for(int k=0;k<4;k++)if(d&bin[k]){x=k;break;}} else {for(int k=0;k<4;k++)if(!(d&bin[k])){x=k;break;}} int y=x^2,cr=x+bh[i][j]; for(int k=0;k<4;k++) if(k!=x)(fx^(ct==3))?add(k+bh[i][j],cr,1+(k==y)):add(cr,k+bh[i][j],1+(k==y)); } // else if(ct==2&&(d&(d>>1)))//L else if(ct==2&&(d!=5&&d!=10))/////caution 9!!! { for(int k=0;k<4;k++) if(d&bin[k])fx?add((k^2)+bh[i][j],k+bh[i][j],1):add(k+bh[i][j],(k^2)+bh[i][j],1); } if(!fx) { if(i>1)add(bh[i][j],2+bh[i-1][j],0); if(j<m)add(1+bh[i][j],3+bh[i][j+1],0); if(i<n)add(2+bh[i][j],bh[i+1][j],0); if(j>1)add(3+bh[i][j],1+bh[i][j-1],0); } } } bool spfa() { memset(ins,0,sizeof ins); memset(dis,0x3f,sizeof dis);dis[0]=0; info[0]=INF;info[t]=0; q.push(0);ins[0]=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]; pre[v]=i;info[v]=Mn(info[k],cap[i]); if(!ins[v])q.push(v),ins[v]=1; } } return info[t]; } int dfs(int cr)//for cr!=0 flow must be 1 { if(cr==t)return 1; ins[cr]=1; for(int& i=cur[cr],v,tmp;i;i=nxt[i]) if(!ins[v=to[i]]&&cap[i]&&dis[v=to[i]]==dis[cr]+w[i]) if(tmp=dfs(v)){ans+=w[i];cap[i]--;cap[i^1]++;return 1;} return 0; } void MCMF() { int tmp; while(spfa()) { memcpy(cur,hd,sizeof hd); while(tmp=dfs(0))flow+=tmp; } } int main() { n=rdn();m=rdn();t=1; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++,t+=4)bh[i][j]=t; init(); if(val&1){puts("-1");return 0;}// MCMF(); printf("%d\n",flow<<1==val?ans:-1);/// return 0; }View Code