网络流刷题日
1.洛谷1343地震逃生
先复习了一波dinic,随便写了个程序
很裸的题目啊,直接建图maxflow再除一除。
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 #define N 205 8 using namespace std; 9 int n,m,x,tot,hd[N],cur[N],vis[N],d[N]; 10 struct edge{int v,next,flow,cap;}e[N*20]; 11 12 void adde(int u,int v,int c){ 13 e[tot].v=v; 14 e[tot].cap=c; 15 e[tot].next=hd[u]; 16 hd[u]=tot++; 17 } 18 19 bool bfs(){ 20 queue<int>q; 21 memset(vis,0,sizeof(vis)); 22 q.push(1);d[1]=0;vis[1]=1; 23 while(!q.empty()){ 24 int u=q.front();q.pop(); 25 for(int i=hd[u];~i;i=e[i].next){ 26 int v=e[i].v; 27 if(e[i].cap-e[i].flow<=0||vis[v])continue; 28 d[v]=d[u]+1;vis[v]=1; 29 q.push(v); 30 } 31 } 32 return vis[n]; 33 } 34 int dfs(int u,int a){ 35 if(!a||u==n)return a; 36 int flow=0,f; 37 for(int &i=cur[u];~i;i=e[i].next){ 38 int v=e[i].v; 39 if(d[u]+1==d[v]&&(f=dfs(v,min(e[i].cap-e[i].flow,a)))){ 40 e[i].flow+=f; 41 e[i^1].flow-=f; 42 flow+=f;a-=f; 43 if(!a)break; 44 } 45 } 46 return flow; 47 } 48 49 int main(){ 50 //freopen(".in","r",stdin); 51 //freopen(".out","w",stdout); 52 memset(hd,-1,sizeof(hd)); 53 scanf("%d%d%d",&n,&m,&x); 54 for(int i=1;i<=m;i++){ 55 int a,b,c; 56 scanf("%d%d%d",&a,&b,&c); 57 adde(a,b,c);adde(b,a,0); 58 } 59 int a1=0,a2=0; 60 while(bfs()){ 61 for(int i=1;i<=n;i++)cur[i]=hd[i]; 62 a1+=dfs(1,2147483647); 63 } 64 if(!a1){ 65 printf("Orz Ni Jinan Saint Cow!"); 66 return 0; 67 } 68 if(x%a1)a2=x/a1+1; 69 else a2=x/a1; 70 printf("%d %d",a1,a2); 71 return 0; 72 }
2. 洛谷1251餐巾纸问题
然后学习了一下zkw费用流,大概就是用dinic跑费用流,,快的飞起
建图还很麻烦。把每天拆成ai,bi两点,ai表示用过的,bi没用过。建立超级源汇S,T。
(1)S向ai连容量r[i],费用0的边,S向bi连容量r[i],费用p的边
(2)bi向T连容量r[i],费用0的边
(3)ai向b(i+m)连容量无穷,费用f的边,ai向b(i+n)连容量无穷,费用f的边,ai向a(i+1)连容量无穷费用0的边
然后zkw 50ms过了
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 #define inf 0x3f3f3f3f 8 #define N 2005 9 using namespace std; 10 int day,n,m,p,s,f,S,T,tot,r[N],cur[N<<1],hd[N<<1],vis[N<<1],d[N<<1]; 11 ll cost; 12 struct edge{int u,v,w,next,cap,flow;}e[N<<4]; 13 void adde(int u,int v,int w,int c){ 14 e[tot].u=u; 15 e[tot].v=v; 16 e[tot].w=w; 17 e[tot].cap=c; 18 e[tot].next=hd[u]; 19 hd[u]=tot++; 20 } 21 22 bool spfa(){ 23 memset(d,0x3f,sizeof(d)); 24 memset(vis,0,sizeof(vis)); 25 deque<int>q;d[T]=0;q.push_back(T); 26 while(!q.empty()){ 27 int u=q.front();q.pop_front();vis[u]=0; 28 for(int i=hd[u];~i;i=e[i].next){ 29 if(e[i^1].cap<=e[i^1].flow)continue; 30 int w=e[i^1].w,v=e[i].v; 31 if(d[v]>d[u]+w){ 32 d[v]=d[u]+w; 33 if(vis[v])continue; 34 vis[v]=1; 35 if(!q.empty()&&d[v]<d[q.front()])q.push_front(v); 36 else q.push_back(v); 37 } 38 } 39 } 40 return d[S]<0x3f3f3f3f; 41 } 42 43 int dfs(int u,int a){ 44 if(!a||u==T)return a; 45 vis[u]=1;int f=0,fl=0; 46 for(int &i=cur[u];~i;i=e[i].next){ 47 int v=e[i].v; 48 if(d[v]+e[i].w==d[u]&&!vis[v]&&(f=dfs(v,min(e[i].cap-e[i].flow,a)))){ 49 cost+=(ll)f*e[i].w;fl+=f; 50 e[i].flow+=f; 51 e[i^1].flow-=f; 52 a-=f; 53 if(!a)break; 54 } 55 } 56 vis[u]=0;return fl; 57 } 58 59 int main(){ 60 #ifdef wsy 61 freopen("data.in","r",stdin); 62 #else 63 //freopen(".in","r",stdin); 64 //freopen(".out","w",stdout); 65 #endif 66 memset(hd,-1,sizeof(hd)); 67 scanf("%d",&day);S=0;T=(day<<1)+1; 68 for(int i=1;i<=day;i++)scanf("%d",&r[i]); 69 scanf("%d%d%d%d%d",&p,&m,&f,&n,&s); 70 for(register int i=1;i<=day;i++){ 71 adde(S,i,0,r[i]);adde(i,S,0,0); 72 adde(S,i+day,p,inf);adde(i+day,S,-p,0); 73 if(i!=day)adde(i,i+1,0,inf),adde(i+1,i,0,0); 74 adde(i+day,T,0,r[i]);adde(T,i+day,0,0); 75 if(i+m<=day)adde(i,i+day+m,f,inf),adde(i+day+m,i,-f,0); 76 if(i+n<=day)adde(i,i+day+n,s,inf),adde(i+day+n,i,-s,0); 77 } 78 while(spfa()){ 79 for(int i=S;i<=T;i++)cur[i]=hd[i]; 80 dfs(S,inf); 81 } 82 printf("%lld",cost); 83 return 0; 84 }
3.bzoj1433假期的宿舍
我的方法跟网上其他博客有点不一样,随便YY的。把每个人拆成2个点ai bi,建立超级源汇S,T。
(1)S向所有 外校人员的ai建立容量1的边
(2)所有 在校的要回家的学生的bi向T连容量为1的边
(3)对于一对相互认识的人bi->aj互相建容量无穷边
(4)每个人的ai向bi建立容量1的边
这样以后一个流就表示一个挪动方案
1 #include2 #include 3 #include 4 #include 5 #include 6 #define inf 0x3f3f3f3f 7 #define ll long long 8 #define N 105 9 using namespace std; 10 int n,S,T,tot,cur[N],st[N],hd[N],gh[N],kn[N][N],d[N],vis[N]; 11 struct edge{int v,next,cap,flow;}e[N*N*4]; 12 void adde(int u,int v,int c){ 13 e[tot].v=v; 14 e[tot].next=hd[u]; 15 e[tot].cap=c; 16 e[tot].flow=0; 17 hd[u]=tot++; 18 } 19 20 bool bfs(){ 21 queue<int>q; 22 memset(vis,0,sizeof(vis)); 23 q.push(S);d[S]=0; 24 while(!q.empty()){ 25 int u=q.front();q.pop();vis[u]=1; 26 for(int i=hd[u];~i;i=e[i].next){ 27 int v=e[i].v; 28 if(e[i].cap<=e[i].flow||vis[v])continue; 29 d[v]=d[u]+1;q.push(v); 30 } 31 } 32 return vis[T]; 33 } 34 35 int dfs(int u,int a){ 36 if(u==T||!a)return a; 37 int fl=0,f; 38 for(int i=hd[u];~i;i=e[i].next){ 39 int v=e[i].v; 40 if(d[v]==d[u]+1&&(f=dfs(v,min(e[i].cap-e[i].flow,a)))){ 41 fl+=f;a-=f; 42 e[i].flow+=f; 43 e[i^1].flow-=f; 44 if(a<=0)break; 45 } 46 } 47 return fl; 48 } 49 int main(){ 50 #ifdef wsy 51 freopen("data.in","r",stdin); 52 #else 53 //freopen(".in","r",stdin); 54 //freopen(".out","w",stdout); 55 #endif 56 int cas;scanf("%d",&cas); 57 while(cas--){ 58 scanf("%d",&n);S=0;T=2*n+1;tot=0; 59 memset(hd,-1,sizeof(hd)); 60 for(int i=1;i<=n;i++)scanf("%d",&st[i]); 61 62 // for(int i=1;i<=n;i++)if(!st[i])printf("%d ",i);puts(""); 63 64 for(int i=1;i<=n;i++)scanf("%d",&gh[i]); 65 66 // for(int i=1;i<=n;i++)if(st[i]&&gh[i])printf("%d ",i);puts(""); 67 68 for(int i=1;i<=n;i++) 69 for(int j=1;j<=n;j++) 70 scanf("%d",&kn[i][j]); 71 for(int i=1;i<=n;i++){ 72 if(st[i]){ 73 if(gh[i])adde(i+n,T,1),adde(T,i+n,0); 74 } 75 else adde(S,i,1),adde(i,S,0); 76 } 77 for(int i=1;i<=n;i++) 78 for(int j=i+1;j<=n;j++){ 79 if(!kn[i][j])continue; 80 // printf("%d %d\n",i,j); 81 int f1=st[i],f2=st[j]; 82 if(f1)adde(j+n,i,inf),adde(i,j+n,0); 83 if(f2)adde(i+n,j,inf),adde(j,i+n,0); 84 } 85 for(int i=1;i<=n;i++) 86 adde(i,i+n,1),adde(i+n,i,0); 87 while(bfs()){ 88 for(int i=S;i<=T;i++)cur[i]=hd[i]; 89 dfs(S,0x3f3f3f3f); 90 } 91 int fg=0; 92 for(int i=hd[S];~i;i=e[i].next) 93 if(e[i].cap-e[i].flow>0)fg=1; 94 if(fg)puts("T_T"); 95 else puts("^_^"); 96 } 97 return 0; 98 }
4.bzoj1934Vote善意的投票
最小割模型。
S向1的人建容量1的边,0的人向T建容量1的边。认识的人互相建容量1的边。
这样求出最小割后,与S相连的点集表示最终选1的人,与T相连表示选0,每条割边表示一个冲突。
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 #define N 305 8 using namespace std; 9 int n,m,S,T,tot,hd[N],d[N],vis[N],cur[N]; 10 struct edge{int v,next,cap,flow;}e[N*N*4]; 11 void adde(int u,int v,int c){ 12 e[tot].v=v; 13 e[tot].next=hd[u]; 14 e[tot].cap=c; 15 e[tot].flow=0; 16 hd[u]=tot++; 17 } 18 19 bool bfs(){ 20 queue<int>q; 21 memset(vis,0,sizeof(vis)); 22 q.push(S);d[S]=0; 23 while(!q.empty()){ 24 int u=q.front();q.pop();vis[u]=1; 25 for(int i=hd[u];~i;i=e[i].next){ 26 int v=e[i].v; 27 if(e[i].cap<=e[i].flow||vis[v])continue; 28 d[v]=d[u]+1;q.push(v); 29 } 30 } 31 return vis[T]; 32 } 33 34 int dfs(int u,int a){ 35 if(u==T||!a)return a; 36 int fl=0,f; 37 for(int i=hd[u];~i;i=e[i].next){ 38 int v=e[i].v; 39 if(d[v]==d[u]+1&&(f=dfs(v,min(e[i].cap-e[i].flow,a)))){ 40 fl+=f;a-=f; 41 e[i].flow+=f; 42 e[i^1].flow-=f; 43 if(a<=0)break; 44 } 45 } 46 return fl; 47 } 48 int main(){ 49 scanf("%d%d",&n,&m); 50 memset(hd,-1,sizeof(hd)); 51 S=0;T=n+1; 52 for(int i=1;i<=n;i++){ 53 int x; 54 scanf("%d",&x); 55 if(x)adde(S,i,1),adde(i,S,0); 56 else adde(i,T,1),adde(T,i,0); 57 } 58 for(int i=1;i<=m;i++){ 59 int a,b; 60 scanf("%d%d",&a,&b); 61 adde(a,b,1);adde(b,a,1); 62 } 63 int flow=0; 64 while(bfs()){ 65 for(int i=S;i<=T;i++)cur[i]=hd[i]; 66 flow+=dfs(S,0x3f3f3f3f); 67 } 68 printf("%d",flow); 69 return 0; 70 }
5.1854: [Scoi2010]游戏
两种解法
(1)二分图
把每个物品和属性都当成点,物品为黑部,属性白部。物品向它有的属性建边。从属性1的点开始向下匹配,匹配到不成功即为答案。
(2)并查集
把属性当成点,物品当成边。那对于每一条边,可以选择他两端的一个点。
如果一个联通块是一棵大小为n的树,我们可以从其中选出n-1个点。否则可以选出所有的点。
记录联通块是不是树和联通块内最大的属性值,合并即可。
选点肯定是先选出小的点来构成1,2,3,4,5……所以判断当一个属性是它所在联通块最大值时,此属性为答案
6.bzoj1066蜥蜴
很裸的最大流啊。拆点,每个格子拆成入点和出点,入点向出点连容量为高度的边。
S向有蜥蜴的格子入点连容量1的边,可以跳出的边界的格子出点向T连容量无穷边。如果a格可以到达b格,a的出点连b的入点,容量无穷。
1 #include2 #include 3 #include 4 #define inf 0x7fffffff 5 using namespace std; 6 struct data{int to,next,v;}e[500001]; 7 int r,c,d,cnt=1,ans,mp[21][21],mark[21][21],q[802],h[802],head[802]; 8 void ins(int u,int v,int w) 9 {cnt++;e[cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].v=w;} 10 void insert(int u,int v,int w) 11 {ins(u,v,w);ins(v,u,0);} 12 bool judge(int x1,int y1,int x2,int y2) 13 { 14 if(((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2))<=(d*d)&&mp[x1][y1]&&mp[x2][y2])return 1; 15 return 0; 16 } 17 void build() 18 { 19 for(int x1=1;x1<=r;x1++) 20 for(int y1=1;y1<=c;y1++) 21 for(int x2=x1-d;x2<=x1+d;x2++) 22 for(int y2=y1-d;y2<=y1+d;y2++) 23 if(judge(x1,y1,x2,y2)&&(x1!=x2||y1!=y2))insert(mark[x1][y1]+400,mark[x2][y2],inf); 24 for(int i=1;i<=r;i++) 25 for(int j=1;j<=c;j++) 26 if(mp[i][j])insert(mark[i][j],mark[i][j]+400,mp[i][j]); 27 } 28 bool bfs() 29 { 30 memset(h,-1,sizeof(h)); 31 int t=0,w=1,i,now;q[0]=h[0]=0; 32 while(t<w) 33 { 34 now=q[t];t++;i=head[now]; 35 while(i) 36 { 37 if(e[i].v&&h[e[i].to]==-1) 38 { 39 h[e[i].to]=h[now]+1; 40 q[w++]=e[i].to; 41 } 42 i=e[i].next; 43 } 44 } 45 if(h[801]==-1)return 0;return 1; 46 } 47 int dfs(int x,int f) 48 { 49 if(x==801)return f; 50 int i=head[x],used=0,w; 51 while(i) 52 { 53 if(e[i].v&&h[e[i].to]==h[x]+1) 54 { 55 w=f-used;w=dfs(e[i].to,min(w,e[i].v)); 56 e[i].v-=w;e[i^1].v+=w; 57 used+=w;if(used==f)return f; 58 } 59 i=e[i].next; 60 } 61 if(!used)h[x]=-1; 62 return used; 63 } 64 void dinic(){while(bfs())ans-=dfs(0,inf);} 65 int main() 66 { 67 scanf("%d%d%d",&r,&c,&d); 68 char ch[21]; 69 for(int i=1;i<=r;i++) 70 { 71 scanf("%s",ch); 72 for(int j=1;j<=c;j++) 73 mp[i][j]=ch[j-1]-'0'; 74 } 75 int tot=0; 76 for(int i=1;i<=r;i++) 77 for(int j=1;j<=c;j++) 78 {tot++;mark[i][j]=tot;} 79 for(int i=1;i<=r;i++) 80 { 81 scanf("%s",ch); 82 for(int j=1;j<=c;j++) 83 if(ch[j-1]=='L') 84 {insert(0,mark[i][j],1);ans++;} 85 } 86 for(int i=1;i<=d;i++) 87 for(int j=d+1;j<=r-d;j++) 88 { 89 insert(mark[j][i]+400,801,inf); 90 insert(mark[j][c-i+1]+400,801,inf); 91 } 92 for(int i=1;i<=d;i++) 93 for(int j=1;j<=c;j++) 94 { 95 insert(mark[i][j]+400,801,inf); 96 insert(mark[r-i+1][j]+400,801,inf); 97 } 98 build(); 99 dinic(); 100 printf("%d",ans); 101 return 0; 102 }
7.bzoj1070修车
费用流。把车看作点,S向每辆车连容量1费用0的点。
把每个工人拆成n个点,k点表示倒数第k个修这辆车。
每个工人点向T连容量1费用0的边。
每辆车向每个工人点连边,容量1,费用为k*cost,k即为这个工人倒数第k个修这辆车。
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 #define N 105 8 using namespace std; 9 int n,m,tot,S,T,base,t[N][N],hd[N*N],vis[N*N],d[N*N],pre[N*N],a[N*N]; 10 struct edge{int u,v,w,next,cap;}e[N*N*6]; 11 bool spfa(int &flow,int &cost){ 12 queue<int>q; 13 memset(pre,-1,sizeof(pre)); 14 memset(d,0x3f,sizeof(d)); 15 q.push(S);d[S]=0;a[S]=0x3f3f3f3f; 16 while(!q.empty()){ 17 int u=q.front();q.pop(); 18 vis[u]=0; 19 for(int i=hd[u];~i;i=e[i].next){ 20 int v=e[i].v; 21 if(e[i].cap>0&&d[v]>d[u]+e[i].w){ 22 d[v]=d[u]+e[i].w;pre[v]=i; 23 a[v]=min(a[u],e[i].cap); 24 if(vis[v])continue; 25 vis[v]=1;q.push(v); 26 } 27 } 28 } 29 if(d[T]==0x3f3f3f3f)return 0; 30 flow+=a[T];cost+=a[T]*d[T]; 31 int u=T; 32 while(u!=S){ 33 e[pre[u]].cap-=a[T]; 34 e[pre[u]^1].cap+=a[T]; 35 u=e[pre[u]].u; 36 } 37 return 1; 38 } 39 void adde(int u,int v,int w,int cap){ 40 e[tot].u=u;e[tot].next=hd[u];e[tot].v=v; 41 e[tot].w=w;e[tot].cap=cap;hd[u]=tot++; 42 } 43 int get(int i,int j){return (j-1)*m+i;} 44 void build(int i,int j,int k){ 45 adde(i+base,get(j,k),k*t[j][i],1); 46 adde(get(j,k),i+base,-k*t[j][i],0); 47 // printf("%d %d %d\n",i+base,get(j,k),k*t[j][i]); 48 } 49 int main(){ 50 scanf("%d%d",&m,&n); 51 memset(hd,-1,sizeof(hd)); 52 base=n*m;S=0;T=base+n+1; 53 for(int i=1;i<=n;i++) 54 for(int j=1;j<=m;j++)scanf("%d",&t[j][i]); 55 for(int i=1;i<=n;i++)adde(S,i+base,0,1),adde(i+base,S,0,0)/*printf("%d %d 0\n",S,i+base)*/; 56 for(int i=1;i<=n;i++)for(int j=1;j<=m;j++) 57 for(int k=1;k<=n;k++)build(i,j,k); 58 for(int i=1;i<=base;i++)adde(i,T,0,1),adde(T,i,0,0)/*printf("%d %d 0\n",i,T)*/; 59 int flow=0,cost=0; 60 while(spfa(flow,cost)); 61 printf("%.2lf",(double)cost/n); 62 return 0; 63 }
8.bzoj1412狼和羊的故事
很裸的最小割模型了。把狼和羊分开即可。
S向狼连容量无穷边,羊向T连容量无穷边。
狼向周围空地和羊连容量1的边,空地向周围空地和羊连容量1边。
最小割后,和S连的点即表示狼的领地,与T相连的点表示羊的领地。
1 #include2 #include 3 #include 4 #include 5 #include 6 #define ll long long 7 #define inf 0x3f3f3f3f 8 #define N 105 9 using namespace std; 10 int n,m,S,T,tot,mp[N][N],hd[N*N],cur[N*N],d[N*N],vis[N*N]; 11 int dx[]={0,0,1,-1};int dy[]={1,-1,0,0}; 12 struct edge{int v,cap,next;}e[N*N*8]; 13 int get(int i,int j){return (i-1)*m+j;} 14 bool check(int i,int j){return i>=1&&i<=n&&j>=1&&j<=m;} 15 void adde(int u,int v,int c){ 16 e[tot].v=v; 17 e[tot].next=hd[u]; 18 e[tot].cap=c; 19 hd[u]=tot++; 20 } 21 bool bfs(){ 22 queue<int>q; 23 memset(vis,0,sizeof(vis)); 24 d[S]=0;q.push(S);vis[S]=1; 25 while(!q.empty()){ 26 int u=q.front();q.pop(); 27 for(int i=hd[u];~i;i=e[i].next){ 28 int v=e[i].v; 29 if(e[i].cap<=0||vis[v])continue; 30 d[v]=d[u]+1;vis[v]=1;q.push(v); 31 } 32 } 33 return vis[T]; 34 } 35 int dfs(int u,int a){ 36 if(u==T||!a)return a; 37 int fl=0,f; 38 for(int &i=cur[u];~i;i=e[i].next){ 39 if(e[i].cap<=0)continue; 40 int v=e[i].v; 41 if(d[v]==d[u]+1&&(f=dfs(v,min(a,e[i].cap)))){ 42 fl+=f;a-=f;e[i].cap-=f; 43 e[i^1].cap+=f;if(!a)break; 44 } 45 } 46 return fl; 47 } 48 int main(){ 49 #ifdef wsy 50 freopen("data.in","r",stdin); 51 #else 52 //freopen(".in","r",stdin); 53 //freopen(".out","w",stdout); 54 #endif 55 scanf("%d%d",&n,&m); 56 memset(hd,-1,sizeof(hd)); 57 S=0;T=n*m+1; 58 for(int i=1;i<=n;i++) 59 for(int j=1;j<=m;j++){ 60 scanf("%d",&mp[i][j]); 61 if(mp[i][j]==1)adde(S,get(i,j),inf),adde(get(i,j),S,0); 62 else if(mp[i][j]==2)adde(get(i,j),T,inf),adde(T,get(i,j),0); 63 } 64 for(int i=1;i<=n;i++) 65 for(int j=1;j<=m;j++){ 66 if(mp[i][j]==2)continue; 67 int idx=get(i,j); 68 for(int k=0;k<4;k++){ 69 int nx=i+dx[k],ny=j+dy[k]; 70 if(!check(nx,ny))continue; 71 int nid=get(nx,ny); 72 if(mp[i][j]==1){ 73 if(mp[nx][ny]!=1)adde(idx,nid,1),adde(nid,idx,0); 74 } 75 else if(mp[nx][ny]!=1)adde(idx,nid,1),adde(nid,idx,0); 76 } 77 } 78 int ans=0; 79 while(bfs()){ 80 for(int i=S;i<=T;i++)cur[i]=hd[i]; 81 ans+=dfs(S,0x3f3f3f3f); 82 } 83 printf("%d",ans); 84 return 0; 85 }
每道题都因为一些zz错误调了很久,还真TM是愉悦的一天。