1、hdu4292
题解:最大流 因为我们发现食物和饮料间没有直接的联系,所以要用人来当中间点,但是如果直接连到人上,会让食物和饮料流出奇怪的情况,所以把人拆点。
人和人之间连边权为1,食物和人 饮料和人都连边为1。
食物和S连食物个数,饮料和T连饮料个数。超级源点->食物->人->人'->饮料->超级汇点。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 0x3f3f3f3f using namespace std; const int N = 20005; const int M = 200005; int n, f, d, s, t; int cnt, head[N], link[N], que[N], lv[N]; int nxt[M], to[M], v[M]; void add(int u,int vv,int w){ to[cnt]=vv,v[cnt]=w; nxt[cnt]=head[u],head[u]=cnt++; to[cnt]=u,v[cnt]=0; nxt[cnt]=head[vv],head[vv]=cnt++; } int bfs(){ int kid,now,f=0,r=1,i; memset(lv,0,sizeof(lv)); que[0]=s; lv[s]=1; while (f<r){ now=que[f++]; for(int i=head[now];i!=-1;i=nxt[i]){ kid=to[i]; if (!lv[kid] && v[i]){ lv[kid]=lv[now]+1; if (kid==t) return 1; que[r++]=kid; } } } return 0; } int dfs(int now,int sum){ int kid,flow,rt=0; if (now==t) return sum; for (int i=link[now];i!=-1 && rt<sum;i=nxt[i]){ link[now]=i; kid=to[i]; if (lv[kid]==lv[now]+1 && v[i]){ flow=dfs(kid,min(sum-rt,v[i])); if (flow){ v[i]-=flow; v[i^1]+=flow; rt+=flow; } else lv[kid]=-1; } } return rt; } char c[205]; void read(){ int a; for (int i=1;i<=f;i++){ scanf("%d",&a); add(s,i,a); } for (int i=1;i<=d;i++){ scanf("%d",&a); add(i+900,t,a); } for (int i=1;i<=n;i++) add(i+300,i+600,1); getchar(); for (int i=1;i<=n;i++){ scanf("%s",c); for (int j=0;j<f;j++) if (c[j]=='Y') add(j+1,i+300,1); } for (int i=1;i<=n;i++){ scanf("%s",c); for (int j=0;j<d;j++) if (c[j]=='Y') add(i+600,j+901,1); } } int dinic(){ int ans=0; while (bfs()){ for (int i=0;i<=t;i++) link[i]=head[i]; ans+=dfs(s,inf); } return ans; } int main(){ while (scanf("%d%d%d",&n,&f,&d)==3){ cnt=0; memset(head,-1,sizeof(head)); s=0,t=1999; read(); printf("%d\n",dinic()); } return 0; }
题目:有一张图,其中n个点,m条双向边,边有权值。所以从起点1走到终点n有一个最短时间。问:
(1)最少去掉多少条边,使得从起点到终点的时间大于最短时间(走不到也算)
(2)最多去掉多少条边,使得从起点到终点的时间仍然为最短时间。
其中 n ≤ 2000; m ≤ 60000
题解:第二问明显是m-(边最少的最短路径上的边数),第一问我们可以联想到是一个最小割,然后最小割最大流定理,构造边流量为1的新图,跑最大流。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 0x3f3f3f3f using namespace std; const int maxn = 2010; const int maxm = 60010; const int N = 2010; const int M = 60010; struct node{ int v,c,flow,nxt; }e[120020]; vector< pair<int, int> > G[N]; queue<int> Q; int head[maxn],cnt,n,m; int st,ed; int dd[maxn],cc[maxn]; void spfa(int st,int ed){ dd[st]=cc[st]=0; bool vis[maxn]={0}; while (!Q.empty()) Q.pop(); vis[st]=1; Q.push(st); while (!Q.empty()){ int u=Q.front(); Q.pop(); vis[u]=0; for (int i=0;i<G[u].size();i++){ int v=G[u][i].first,c=G[u][i].second; if (dd[u]+c==dd[v]) cc[v]=min(cc[v],cc[u]+1); if (dd[u]+c<dd[v]){ cc[v]=cc[u]+1; dd[v]=dd[u]+c; if (!vis[v]) Q.push(v), vis[v]=1; } } } } void add(int u,int v,int c){ e[++cnt].v=v; e[cnt].c=c; e[cnt].flow=0; e[cnt].nxt=head[u]; head[u]=cnt; e[++cnt].v=u; e[cnt].c=0; e[cnt].flow=0; e[cnt].nxt=head[v]; head[v]=cnt; } void init(){ memset(head,-1,sizeof(head)); cnt=1; st=1, ed=n; for (int u=1;u<=n;u++){ for (int i=0;i<G[u].size();i++){ if (dd[u]+G[u][i].second==dd[G[u][i].first]) add(u,G[u][i].first,1); } } } int lv[maxn],num[maxn],q[maxn]; void bfs(){ int f=0,r=1; for (int i=0;i<=n;i++){ lv[i]=0; num[i]=0; } q[f]=st; lv[st]=1; num[st]=1; while (f!=r){ int u=q[f++]; for (int i=head[u];i;i=e[i].nxt){ if (e[i^1].c==0 || lv[e[i].v]<maxn) continue; lv[e[i].v]=lv[u]+1; ++num[lv[e[i].v]]; q[r++]=e[i].v; } } } int dinic(){ n+=3; bfs(); int u,flow=0; int cur[maxn],path[maxn]; for (int i=0;i<=n;i++) cur[i]=head[i]; u=st; while (lv[st]<n){ if (u==ed){ int augflow=inf; for (int i=st;i!=ed;i=e[cur[i]].v) augflow=min(augflow,e[cur[i]].c); for (int i=st;i!=ed;i=e[cur[i]].v){ e[cur[i]].c-=augflow; e[cur[i]^1].c+=augflow; e[cur[i]].flow+=augflow; e[cur[i]^1].flow-=augflow; } flow+=augflow; u=st; } int i; for (i=cur[u];i;i=e[i].nxt) if(e[i].c>0 && lv[u]==lv[e[i].v]+1) break; if (i){ cur[u]=i; path[e[i].v]=i^1; u=e[i].v; } else { if (0==(--num[lv[u]])) break; cur[u]=head[u]; int mindis=n; for (int j=head[u];j;j=e[j].nxt) if(e[j].c>0) mindis=min(mindis,lv[e[j].v]); lv[u]=mindis+1; ++num[lv[u]]; if (u!=st) u=e[path[u]].v; } } n-=3; return flow; } int main(){ while (~scanf("%d%d",&n,&m)){ for (int i=1;i<=n;i++){ G[i].clear(); dd[i]=cc[i]=inf; } int u,v,l; for (int i=0;i<m;i++){ scanf("%d%d%d",&u,&v,&l); G[u].push_back({v,l}); G[v].push_back({u,l}); } spfa(1,n); init(); printf("%d %d\n",dinic(),m-cc[n]); } return 0; }
题目:有n个城市,有个小偷想从其中一个城市逃到另一个城市,警察想要堵截这个小偷,知道了在每个城市堵截的成本,问如何安排在哪些城市堵截可以使小偷一定会被抓住,而且成本最低。
题解:还是最小割模型,把城市拆点,然后有边的两个城市连inf,防止直接流过去。
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; struct edge{ int to,w,nxt; }e[20005*8]; int lv[2005],que[2005],val[2005]; int head[2005],cnt; void add(int u,int v,int w){ e[cnt].to=v; e[cnt].w=w; e[cnt].nxt=head[u]; head[u]=cnt++; e[cnt].to=u; e[cnt].w=0; e[cnt].nxt=head[v]; head[v]=cnt++; } int bfs(int s,int t){ memset(lv,0,sizeof(lv)); int f=0,tail=1; lv[s]=1; que[f]=s; while (f<tail){ int now=que[f++]; for (int i=head[now];i!=-1;i=e[i].nxt){ if (e[i].w && lv[e[i].to]==0){ lv[e[i].to]=lv[now]+1; if (e[i].to==t) return 1; que[tail++]=e[i].to; } } } return 0; } int dfs(int s,int t,int flow){ if (s==t) return flow; int tmp,cost=0; for (int i=head[s];i!=-1;i=e[i].nxt){ if (e[i].w && lv[e[i].to]==lv[s]+1){ tmp=dfs(e[i].to,t,min(flow-cost,e[i].w)); if (tmp>0){ e[i].w-=tmp; e[i^1].w+=tmp; cost+=tmp; if (flow==cost) break; } else lv[e[i].to]=-1; } } return cost; } int dinic(int s,int t){ int ans=0; while (bfs(s,t)) ans+=dfs(s,t,inf); return ans; } int main(){ int n,m,s,t; while (~scanf("%d%d",&n,&m)){ cnt=0; memset(head,-1,sizeof(head)); scanf("%d%d",&s,&t); for (int i=1;i<=n;i++){ scanf("%d",&val[i]); add(i,i+1000,val[i]); } int u,v; while (m--){ scanf("%d%d",&u,&v); add(u+1000,v,inf); add(v+1000,u,inf); } int ss=2003,tt=2004; add(ss,s,inf); add(t+1000,tt,inf); s=ss,t=tt; printf("%d\n",dinic(s,t)); } return 0; }
题目:给一个N行M列的数字矩阵的行和以及列和,每个元素的大小不超过9,问这样的矩阵是否存在,是否唯一。
题解:这个题问是否存在很好想,S到行和连行和,列和到T连列和,行和到列和连9,但是问是否唯一这里我真的想了好久……之前一直觉得复杂度不对……后来发现要对残量网络删边建新图然后找环就是O(M)的了,如果不建新图,每条边要被判(N-1)*(M-1)+1次……为什么找环就能确定唯一呢……因为如果你图上有一个流量为正点数为偶数的环,说明数值可以在其中两个点上流动,所以就不唯一。
代码:
#include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<iostream> #include<queue> #include<vector> #include<set> #include<stack> #include<map> #include<ctime> #include<bitset> #define LL long long #define db double #define EPS 1e-15 #define inf 1e10 using namespace std; const int MAXN=500*2+10; const int MAXM=500*500+2*MAXN; int n,m,sum; int S,T; int head[MAXN],cnt; int cur[MAXN],lv[MAXN]; bool flag,only; struct edge{ int from,to,w,flow,nxt; }e[MAXM<<1]; void add(int u,int v,int w){ e[cnt].from=u; e[cnt].to=v; e[cnt].w=w; e[cnt].flow=0; e[cnt].nxt=head[u]; head[u]=cnt++; e[cnt].from=v; e[cnt].to=u; e[cnt].w=0; e[cnt].flow=0; e[cnt].nxt=head[v]; head[v]=cnt++; } bool bfs(){ puts("yes"); memset(lv,-1,sizeof(lv)); queue<int>q; q.push(S); lv[S]=0; while (!q.empty()){ int now=q.front(); q.pop(); for (int i=head[now];i!=-1;i=e[i].nxt){ int v=e[i].to; if (lv[v]==-1 && e[i].w>e[i].flow){ lv[v]=lv[now]+1; q.push(v); } } } return lv[T]!=-1; } int dfs(int u,int cap){ if (u==T || cap==0) return cap; int flow=0,subflow; for (int i=cur[u];i!=-1;i=e[i].nxt){ cur[u]=i; int v=e[i].to; if ((lv[v]==lv[u]+1) && (subflow=dfs(v,min(cap,e[i].w-e[i].flow)))>0){ flow+=subflow; e[i].flow+=subflow; e[i^1].flow-=subflow; cap-=subflow; if(cap==0) break; } } return flow; } int dinic(){ int maxflow=0; while(bfs()){ memcpy(cur,head,sizeof(head)); maxflow+=dfs(S,inf); } return maxflow; } void check1(){ if (flag) return ; if (dinic()!=sum){ flag=1; } } bool vis[MAXN]; bool checkO(int x,int f){ vis[x]=1; for (int i=head[x];i;i=e[i].nxt){ int v=e[i].to; if (v!=f){ if (vis[v]) return 1; if (checkO(v,x)) return 1; } } vis[x]=0; return 0; } void checkonly(){ if (flag) return ; memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++){ if (checkO(i,-1)){ only=0; return ; } } } void add2(int u,int v){ e[cnt].to=v; e[cnt].nxt=head[u]; head[u]=cnt++; } void rebuild(){ memset(head,-1,sizeof(head)); int tot=cnt; cnt=0; for (int i=0;i<tot;i++){ if (e[i].w>e[i].flow){ add2(e[i].from,e[i].to); } } } int main(){ int T,cas=1; scanf("%d",&T); while (T--){ scanf("%d %d",&n,&m); flag=0,only=1; cnt=0; memset(head,-1,sizeof(head)); int r,c,rsum=0,csum=0; S=0; T=n+m+1; for (int i=1;i<=n;++i){ scanf("%d",&r); rsum+=r; add(S,i,r); } for (int i=1;i<=m;++i){ scanf("%d",&c); csum+=c; add(i+n,T,c); } if (rsum!=csum){ flag=1; break; } sum=rsum; for (int i=1;i<=n;i++){ for (int j=m;j>=1;j--){ add(i,j+n,9); } } check1(); rebuild(); checkonly(); printf("Case #%d: ",cas++); if(flag) puts("So naive!"); else if (only) puts("So simple!"); else puts("So young!"); } return 0; }