本文共 38702 字,大约阅读时间需要 129 分钟。
【BZOJ 1066】
每个格子有一定的限制,所以每个格子拆成两个点,然后限流即可。
UPD:垃圾题目,因为标号错误调了一天。
#include #include #include #include #include #define INF 10000000#define M(a) memset(a,0,sizeof a)#define F(i,j,k) for (int i=j;i<=k;++i)using namespace std;int list[51][51],num[51][51];int n,m,d,en=0;int S=0,T,sum=0,all=0;int u[100001],v[100001],h[100001],ne[100001],f[100001],map[100001];inline void add(int a,int b,int r){ u[en]=a;v[en]=b;ne[en]=h[a];f[en]=r;h[a]=en++; u[en]=b;v[en]=a;ne[en]=h[b];f[en]=0;h[b]=en++;}inline bool tell(){ memset(map,-1,sizeof map); queue q; q.push(S); map[S]=0; while (!q.empty()){ int u=q.front(); q.pop(); for (int i=h[u];i!=-1;i=ne[i]){ if (map[v[i]]==-1&&f[i]){ map[v[i]]=map[u]+1; q.push(v[i]); } } } if (map[T]!=-1) return true; else return false;}inline int zeng (int k,int now)//进行增广路{ if (k==T) return now; int r=0; for (int i=h[k];i!=-1&&now>r;i=ne[i]){ if (map[k]+1==map[v[i]]&&f[i]!=0){ int t=zeng(v[i],min(now-r,f[i])); f[i]-=t;f[i^1]+=t;r+=t; } } if (!r) map[k]=-1; return r;}inline int dinic(){ int r=0,t; while (tell()) while (t=zeng(S,INF)) r+=t; return r; }inline bool ok(int a,int b,int c,int e){ if ((c-a)*(c-a)+(e-b)*(e-b)<=d*d) return true; return false;}int main(){ memset(h,-1,sizeof h); memset(ne,-1,sizeof ne); memset(u,-1,sizeof u); memset(v,-1,sizeof v); scanf("%d%d%d",&n,&m,&d); F(i,1,n)F(j,1,m) { char ch; cin>>ch; list[i][j]=ch-'0'; if (list[i][j]!=0){ num[i][j]=++sum; } } T=sum*2+1; F(i,1,n)F(j,1,m) { char ch; cin>>ch; if (ch=='L') { add(S,num[i][j]*2,1); all++; } } F(i,1,n)F(j,1,m) if ((i<=d||j<=d||i+d>n||j+d>m)&&(num[i][j])){ add(num[i][j]*2+1,T,INF); } F(i,1,n)F(j,1,m) if (num[i][j]) { add(num[i][j]*2,num[i][j]*2+1,list[i][j]); } F(i,1,n)F(j,1,m){ if (num[i][j]){ F(x,min(i-d,1),min(i+d,n)) F(y,min(j-d,1),min(j+d,m)) if (num[x][y]&&(i!=x||j!=y)) if (ok(i,j,x,y)) { add(num[i][j]*2+1,num[x][y]*2,INF); } } } cout< <
【BZOJ 1077】
最大权闭合子图的变形,增加了一个租用的方法。只需要在工作和机器之间加入一条流量为租用的边就可以最小割求解了。
UPD:垃圾题目,TLE了实在优化不过去了。
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define ll long long#define maxm 400005#define maxn 10005 int h[maxm],to[maxm],fr[maxm],ne[maxm],fl[maxm],en=0,cur[maxn]; void add(int a,int b,int c){ to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=c;h[a]=en++; to[en]=a;fr[en]=b;ne[en]=h[b];fl[en]=0;h[b]=en++;} int n,m,S=0,T=maxn-1,ans,dis[maxn];queue q;int read(){ int x=0,f=1; char ch=getchar(); while (ch<'0'||ch>'9') {if (ch=='-') f=-1; ch=getchar();} while (ch>='0'&&ch<='9') {x=x*10+ch-'0'; ch=getchar();} return x*f;}bool tell(){ memset(dis,-1,sizeof dis); dis[S]=0;while (!q.empty()) q.pop(); q.push(S); while (!q.empty()) { int x=q.front();q.pop(); for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } } F(i,1,n+m) cur[i]=h[i]; cur[T]=h[T]; cur[S]=h[S]; if (dis[T]==-1) return false; return true;}int zeng(int k,int now){ if (k==T) return now; int r=0; for (int i=cur[k];i>=0&&now>r;i=ne[i]) { if (dis[to[i]]==dis[k]+1&&fl[i]>0){ int tmp=zeng(to[i],min(fl[i],now-r)); fl[i]-=tmp;fl[i^1]+=tmp;r+=tmp;// if (fl[i]) cur[k]=i; } } if (!r) dis[k]=-1; return r;}int main(){ memset(h,-1,sizeof h); n=read();m=read(); F(i,1,n) { int x,y,z; x=read(); add(S,i,x); ans+=x; x=read(); F(j,1,x) { y=read();z=read(); add(i,n+y,z); } } F(i,1,m) { int x; x=read(); add(n+i,T,x); } int tmp; while (tell()) while (tmp=zeng(S,inf)) ans-=tmp; printf("%d\n",ans);}
【BZOJ 1412】
水题,S连羊,T连狼,然后向四周连一条1的边,然后最小割即可。
UPD:人太懒,懒得写
【BZOJ 1433】
需要的和不需要的分在两边,认识的连一条边,然后最大匹配即可。(可以用网络流水过去)
还是太懒
【BZOJ 1475】
棋盘黑白染色,然后就是二分图了,相邻的不能取,就是最大点独立集的模型了。
懒
【BZOJ 1497】
最大权闭合子图裸题。直接1A
#include #include #include #include //#include
q; memset(map,-1,sizeof map); map[S]=0; while (!q.empty()) q.pop(); q.push(S); while (!q.empty()) { int x=q.front(); q.pop();// cout<<"bfs"<
< =0;i=ne[i]) {// cout<<"to "< < 0) { map[to[i]]=map[x]+1; q.push(to[i]); } } }// cout<<"over"< =0&&ret 0) { int tmp=zeng(to[i],min(fl[i],r-ret)); ret+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (!ret) map[k]=-1; return ret;} int n,m,x,a,b,c;ll ans=0; int main(){ memset(h,-1,sizeof h); Finout(); n=Getint(); m=Getint(); F(i,1,n) { x=Getint(); add(i+m,T,x); } F(i,1,m) { a=Getint(); b=Getint(); c=Getint(); add(S,i,c); add(i,m+a,inf); add(i,m+b,inf); ans+=c; }// cout<<"add over"< 【BZOJ 1520】
最小权匹配,可选的标号和学校之间连流量为1,费用为代价,然后最小费用最大流即可。
懒入膏肓。
【BZOJ 1532】
二分答案就可以求了,比赛和选手形成了二分图。
懒
【BZOJ 1565】
PVZ,发现环上的点是不能取的,删去,然后把所有保护关系反向,就形成了最大权闭合子图的模型。然后,就没有然后了。
#include #include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 800005#define maxn 805 int h[maxm],to[maxm],ne[maxm],fl[maxm],en=0,S=maxn-2,T=maxn-1; void add(int a,int b,int r){// printf("add %d - %d is %d\n",a,b,r); to[en]=b; ne[en]=h[a]; fl[en]=r; h[a]=en++; to[en]=a; ne[en]=h[b]; fl[en]=0; h[b]=en++;} int dis[maxn];queue q; bool tell(){ memset(dis,-1,sizeof dis);dis[S]=0;// while (!q.empty()) q.pop(); q.push(S); while (!q.empty()) { int x=q.front(); q.pop();// printf("bfs on %d\n",x); for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } return dis[T]!=-1;} int zeng(int k,int now){ if (k==T) return now; int r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) if (dis[to[i]]==dis[k]+1&&fl[i]){ int tmp=zeng(to[i],min(fl[i],now-r)); r+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (!r) dis[k]=-1; return r;} int hasher[105][105],cnt=0,n,m,du[maxn],top[maxn],ans;vector v[maxn];queue qu;int sco[maxn]; void topsort(){// while (!qu.empty()) qu.pop(); F(i,1,cnt) if (!du[i]) qu.push(i); while (!qu.empty()) { int x=qu.front(); qu.pop(); top[x]=1;// printf("tupo on %d\n",x); for (int i=0;i =0) { ans+=sco[i]; add(S,i,sco[i]); } else add(i,T,-sco[i]); for (int j=0;j
【BZOJ 1585】(吼题)
拆点网络流,每个点拆成两个,如果没有损坏,中间连正无穷,否则连一。损坏的点向T连正无穷使得它和x割开。然后无向边互相连接即可。
#include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 100005#define maxn 6005 int h[maxm],fr[maxm],to[maxm],ne[maxm],fl[maxm],en=0,S=maxn-2,T=maxn-1; void add(int a,int b,int r){to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=r;h[a]=en++;} int dis[maxn];queue q; bool tell(){ memset(dis,-1,sizeof dis);dis[S]=0; q.push(S); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } return dis[T]!=-1;} int zeng(int k,int now){ if (k==T) return now; int r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) if (dis[to[i]]==dis[k]+1&&fl[i]){ int tmp=zeng(to[i],min(fl[i],now-r)); r+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (!r) dis[k]=-1; return r;}int n,hav[maxn],m,k;int main(){ memset(h,-1,sizeof h); scanf("%d%d%d",&n,&m,&k); F(i,1,m) { int x,y; scanf("%d%d",&x,&y); add(x+n,y,inf);add(y,x+n,0); add(y+n,x,inf);add(x,y+n,0); } F(i,1,k) { int x;scanf("%d",&x); add(x+n,T,inf);add(T,x+n,0); hav[x]=1; } F(i,1,n) { if (hav[i]||i==1) { add(i,i+n,inf); add(i+n,i,0); } else { add(i,n+i,1); add(n+i,i,0); } } add(S,1,inf);add(1,S,0); int tmp,ans=0; while (tell()) while (tmp=zeng(S,inf)) ans+=tmp; printf("%d\n",ans);}
【BZOJ 1711】
好题,每个东西要匹配两个,那么只需要改成奶牛在中间,跑三分图匹配即可。
【BZOJ 1741】
如果(x,y)存在,那么在x行,y列之间连边,就成了二分图最小点覆盖。
【BZOJ 1779】(吼题)
J拆成3个点,E拆成两个表示限流,T直接连向汇点。
J的第一个点表示流入,第二个点表示经过,第三个点表示攻击
【BZOJ 1797】
最小割的充分性和必要性
jcvb:
在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。
①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。
①
<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。
②
<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。
Expand:
判定最小割是否唯一,只需要正反两次DFS,然后记录每一个点是否被dfs到,然后如果仍然有点没有被dfs到,就说明最小割不唯一了
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 200005#define maxn 4005 int h[maxm],fr[maxm],to[maxm],ne[maxm],fl[maxm],en=0,S=maxn-2,T=maxn-1; void add(int a,int b,int r){to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=r;h[a]=en++;} int dis[maxn];queue q; bool tell(){ memset(dis,-1,sizeof dis);dis[S]=0; q.push(S); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } return dis[T]!=-1;} int zeng(int k,int now){ if (k==T) return now; int r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) if (dis[to[i]]==dis[k]+1&&fl[i]){ int tmp=zeng(to[i],min(fl[i],now-r)); r+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (!r) dis[k]=-1; return r;} int n,m,vcnt; int dfn[maxn],low[maxn],idx=0,sta[maxn],top=0,id[maxn],ins[maxn]; void tarjan(int k){ dfn[k]=low[k]=++idx; sta[++top]=k; ins[k]=1; for (int i=h[k];i>=0;i=ne[i]) if (fl[i]){ if (!dfn[to[i]]) { tarjan(to[i]); low[k]=min(low[k],low[to[i]]); } else if (ins[to[i]]) low[k]=min(low[k],dfn[to[i]]); } int x=-1; if (dfn[k]==low[k]) { ++vcnt; while (x!=k) { x=sta[top--]; ins[x]=0; id[x]=vcnt; } }} int main(){ memset(h,-1,sizeof h); scanf("%d%d%d%d",&n,&m,&S,&T); F(i,1,m) { int a,b,c; scanf("%d%d%d",&a,&b,&c); add(a,b,c);add(b,a,0); } while (tell()) while (zeng(S,inf)); F(i,1,n) if (!dfn[i]) tarjan(i); for (int i=0;i
【BZOJ 1822】
二分答案,判断能否攻击比较复杂,懒得写。
【BZOJ 1834】(吼)
先跑最大流,然后加上一条无限大的费用的弧,然后增广即可。
【BZOJ 1877】
拆点限制每个点经过一次,然后费用流算一遍就可以了。
【BZOJ 1934】
经典的二元关系的问题,先根据初始意愿建立二分图,然后好朋友之间连1的双向边。然后最小割就是答案。
【BZOJ 1937】(吼)
非树边一定要大于覆盖的树边,然后我们把树边和非树边之间连上一条流量为一,费用为差值的边(代表修改的最小代价),然后用网络流跑最大权匹配,但是不是最大流。
#include #include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 200005#define maxn 1005 int h[maxn],to[maxm],ne[maxm],fl[maxm],cost[maxm],en=0;int S=maxn-2,T=maxn-1; void add(int a,int b,int r,int c){// printf("Add %d %d %d %d\n",a,b,r,c); to[en]=b;ne[en]=h[a];fl[en]=r;cost[en]=c;h[a]=en++;// printf("Add %d %d %d %d\n",b,a,0,-c); to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;} int n,m,a[55][55],id[55][55],fa[55],dep[55],b[55][55]; void dfs(int o,int fat){// printf("dfs on %d %d\n",o,fat); fa[o]=fat; F(i,1,n) if (b[o][i]&&i!=fat) { dep[i]=dep[o]+1; dfs(i,o); }} int dis[maxn],minn[maxn],inq[maxn],with[maxn];queue q; bool tell(){ while (!q.empty()) q.pop(); memset(dis,192,sizeof dis); int flag=0;dis[S]=0; q.push(S);inq[S]=1;minn[S]=inf; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0;// printf("now is %d\n",x);// getchar();// if (x==T) flag=1; for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]] 0) { dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!inq[to[i]]) { q.push(to[i]); inq[to[i]]=1; } } } return dis[T]!=dis[0];} int zeng(){// printf("star to zeng\n"); for (int i=T;i!=S;i=to[with[i]^1]) {// printf("at edge %d -= %d\n",with[i],minn[T]); fl[with[i]]-=minn[T];// printf("at edge %d += %d\n",with[i]^1,minn[T]); fl[with[i]^1]+=minn[T]; } return dis[T]*minn[T];} int main(){ memset(h,-1,sizeof h); scanf("%d%d",&n,&m); F(i,1,m) { int x,y,z; scanf("%d%d%d",&x,&y,&z); a[x][y]=a[y][x]=z; id[x][y]=id[y][x]=i; } F(i,1,n-1) { int x,y; scanf("%d%d",&x,&y); b[x][y]=b[y][x]=a[x][y]; a[x][y]=a[y][x]=0; } dfs(1,0); F(i,1,n-1) F(j,i+1,n) if (a[i][j]){// printf("id is %d\n",id[i][j]);// add(id[i][j],T,1,0); int x=i,y=j; if (dep[x] a[i][j]) add(id[x][fa[x]],id[i][j],1,b[x][fa[x]]-a[i][j]); x=fa[x]; } while (x!=y) {// printf("%d %d \n",x,y); if (b[x][fa[x]]>a[i][j]) add(id[x][fa[x]],id[i][j],1,b[x][fa[x]]-a[i][j]); if (b[y][fa[y]]>a[i][j]) add(id[y][fa[y]],id[i][j],1,b[y][fa[y]]-a[i][j]); x=fa[x]; y=fa[y]; } }// F(i,1,n) if (fa[i]) add(S,id[i][fa[i]],1,0); F(i,1,n-1) F(j,i+1,n) { if (b[i][j]) add(S,id[i][j],1,0); if (a[i][j]) add(id[i][j],T,1,0); } int ans=0; while (tell()&&dis[T]>0) ans+=zeng(); printf("%d\n",ans);}
【BZOJ 2039】
二元关系的基本模型,但是题意要理解清楚,再进行计算。
见图见代码
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 1000000000000#define ll long long#define maxm 5000005#define maxn 1010 int h[maxm],to[maxm],ne[maxm],en=0,S=maxn-2,T=maxn-1;ll fl[maxm]; void add(int a,int b,ll r){ to[en]=b; ne[en]=h[a]; fl[en]=r; h[a]=en++; to[en]=a; ne[en]=h[b]; fl[en]=0; h[b]=en++;} int dis[maxn];queue q; bool tell(){ memset(dis,-1,sizeof dis);dis[S]=0; q.push(S); while (!q.empty()) { int x=q.front(); q.pop(); for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } return dis[T]!=-1;} ll zeng(int k,ll now){ if (k==T) return now; ll r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) if (dis[to[i]]==dis[k]+1&&fl[i]){ ll tmp=zeng(to[i],min(fl[i],now-r)); r+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (!r) dis[k]=-1; return r;} int a[maxn],n,e[maxn][maxn];ll toT[maxn],ans,tmp; int main(){ memset(h,-1,sizeof h); scanf("%d",&n); F(i,1,n) scanf("%d",&a[i]),add(S,i,a[i]),add(i,S,0); F(i,1,n) F(j,1,n) scanf("%d",&e[i][j]); F(i,1,n-1) F(j,i+1,n) { add(i,j,2LL*e[i][j]),add(j,i,2LL*e[i][j]); toT[i]+=e[i][j]; toT[j]+=e[i][j]; ans+=2LL*e[i][j]; } F(i,1,n) add(i,T,toT[i]),add(T,i,0); while (tell()) while (tmp=zeng(S,inf)) ans-=tmp; printf("%lld\n",ans);}
【BZOJ 2127】
二元关系见图即可,边数较多,合并之后再一块加入。
#include #include //#include //#include //#include
【POJ 2699】
肯定存在一种方案使得前n个队伍获胜最多,然后二分答案进行判定即可。
【BZOJ 1189】
枚举时间T,然后加入一些点代表T时刻的点,然后最大流进行判定即可。
二分答案会T
#include #include #include #include #include //#include
【CODEVS 1034】
同样枚举时间,然后加入新的点判断是否可行
UPD:调试代码爽到飞起
#include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxn 500005int n,m,k,p[25],flag=0,S=0,T=maxn-1,ans=0;int sta[25][25],num[25];int h[maxn],to[maxn],ne[maxn],fl[maxn],fr[maxn],en=0;int hash[25][500],cnt=0,dis[maxn];queue q;void add(int a,int b,int c){// printf("Add %d to %d is %d at %d/%d\n",a,b,c,en,en+1); to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=c;h[a]=en++; to[en]=a;fr[en]=b;ne[en]=h[b];fl[en]=0;h[b]=en++;}bool tell(){ memset(dis,-1,sizeof dis); while (!q.empty()) q.pop(); dis[S]=0;q.push(S); while (!q.empty()) { int x=q.front();q.pop();// printf("now is %d\n",x); for (int i=h[x];i>=0;i=ne[i]) {// printf("can to %d by %d\n",to[i],i); if (fl[i]>0&&dis[to[i]]==-1) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } } if (dis[T]==-1) return false; return true;}int zeng(int k,int now){// printf("zeng %d %d\n",k,now); if (k==T) {// printf("successful %d\n",k); return now; } int r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) {// printf("dis %d to %d can to %d fl is %d\n",dis[k],dis[to[i]],to[i],fl[i]); if (dis[k]+1==dis[to[i]]&&fl[i]>0) { int t=zeng(to[i],min(now-r,fl[i])); fl[i]-=t;fl[i^1]+=t;r+=t; } } if (!r) dis[k]=-1; return r;}int main(){ memset(h,-1,sizeof h); scanf("%d%d%d",&n,&m,&k); F(i,1,m) {// printf("chuan %d\n",i); scanf("%d",&p[i]); scanf("%d",&num[i]); F(j,0,num[i]-1) { scanf("%d",&sta[i][j]); if (sta[i][j]==-1) sta[i][j]=n+1; }// F(j,0,num[i]-1) printf("%d ",sta[i][j]); printf("\n"); } F(i,0,n+1) F(j,0,205) hash[i][j]=++cnt;// F(i,0,n+1) F(j,0,10)// printf("%d:%d hash num is %d\n",j,i,hash[i][j]); add(S,hash[0][0],k);// printf("add S to %d:%d with %d\n",0,0,k); add(hash[n+1][0],T,inf);// printf("add %d:%d to T with %d\n",0,n+1,inf); for (int z=0;z<=200;++z) { int tmp; F(i,0,n+1) { add(hash[i][z],hash[i][z+1],inf);// printf("add D(%d):%d to D(%d):%d with %d\n",z,i,z+1,i,inf); } F(i,1,m) { add(hash[sta[i][z%num[i]]][z],hash[sta[i][(z+1)%num[i]]][z+1],p[i]); } add(hash[n+1][z+1],T,inf);// printf("add D(%d):%d to T with %d\n",z+1,n+1,inf); while (tell()) while (tmp=zeng(S,inf)) ans+=tmp;// printf("After Day %d is %d\n",z,ans); if (ans==k) { flag=1; printf("%d\n",z+1); break; } } if (!flag) printf("%d\n",0);}
【BZOJ 2007】
最小割转最短路。
#include #include #include #include #include using namespace std; #define maxn 2000005 int h[maxn],to[maxn],ne[maxn],w[maxn],en=0; void add(int a,int b,int c){// printf("add %d to %d is %d\n",a,b,c); to[en]=b; ne[en]=h[a]; w[en]=c; h[a]=en++;} int n,S=0,T=maxn-1;int hash[605][605],cnt=0;int inq[maxn],dis[maxn]; void SPFA(){ queue q; memset(dis,0x3f,sizeof dis); while (!q.empty()) q.pop(); q.push(S); inq[S]=1; dis[S]=0; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0;// printf("%d\n",x); for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]>dis[x]+w[i]) { dis[to[i]]=dis[x]+w[i]; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } printf("%d\n",dis[T]);} int main(){ memset(h,-1,sizeof h); scanf("%d",&n); for (int i=0;i<=n+1;++i) for (int j=0;j<=n+1;++j) hash[i][j]=++cnt; for (int i=0;i<=n+1;++i) hash[0][i]=maxn-1; for (int i=0;i<=n+1;++i) hash[n+1][i]=0; for (int i=0;i<=n+1;++i) hash[i][0]=0; for (int i=0;i<=n+1;++i) hash[i][n+1]=maxn-1;// for (int i=0;i<=n+1;++i)// {// for (int j=0;j<=n+1;++j)// printf("%d ",hash[i][j]);// printf("\n");// } for (int i=1;i<=n+1;++i) for (int j=1;j<=n;++j) { int tmp; scanf("%d",&tmp); add(hash[i][j],hash[i-1][j],tmp); } for (int i=1;i<=n;++i) for (int j=1;j<=n+1;++j) { int tmp; scanf("%d",&tmp); add(hash[i][j-1],hash[i][j],tmp); } for (int i=1;i<=n+1;++i) for (int j=1;j<=n;++j) { int tmp; scanf("%d",&tmp); add(hash[i-1][j],hash[i][j],tmp); } for (int i=1;i<=n;++i) for (int j=1;j<=n+1;++j) { int tmp; scanf("%d",&tmp); add(hash[i][j],hash[i][j-1],tmp); } SPFA();}
【BZOJ 2561】
一条边再最小生成树上当且仅当不存在一条由权值小于它的边的通路连接他两端。
然后最大生成树同理。相加即为答案。
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxn 400005 int h[maxn],to[maxn],ne[maxn],fl[maxn],fr[maxn],en=0; void add(int a,int b,int c){to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=c;h[a]=en++;} int l[maxn],r[maxn],w[maxn],S,T,nw,ans=0,n,m;int dis[maxn],q[maxn],hd,tl;//queue q; int read(){ int x=0; char ch=getchar(); while (ch<'0' || ch>'9') ch=getchar(); while (ch>='0' && ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x;} bool tell(){ memset(dis,-1,sizeof dis); dis[S]=0; hd=0,tl=0; q[tl++]=S; while (hd =0;i=ne[i]) { if (dis[to[i]]==-1&&fl[i]>0) { dis[to[i]]=dis[x]+1; q[tl++]=to[i]; } } } if (dis[T]==-1) return false; else return true;} int zeng(int k,int now){ if (k==T) return now; int r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) { if (dis[to[i]]==dis[k]+1&&fl[i]>0) { int tmp=zeng(to[i],min(fl[i],now-r)); fl[i]-=tmp; fl[i^1]+=tmp; r+=tmp; } } if (!r) dis[k]=-1; return r;} void solve(){int tmp;while (tell()) while (tmp=zeng(S,inf)) ans+=tmp;} int main(){ n=read();m=read(); F(i,1,m)l[i]=read(),r[i]=read(),w[i]=read(); S=read();T=read();nw=read(); memset(h,-1,sizeof h);en=0;// memset(fl,0,sizeof fl);// memset(ne,0,sizeof ne); F(i,1,m) if (w[i] nw) {add(l[i],r[i],1);add(r[i],l[i],1);} solve(); printf("%d\n",ans);}
【BZOJ 1927】
每个点经过一次,采用拆点的方法,然后瞬间移动看作一条路径,S->u (瞬移费用)。u->T (0)
然后根据原图建出其余的边
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxn 50005 int h[maxn],to[maxn],ne[maxn],fl[maxn],cost[maxn],fr[maxn],en=0; void add(int a,int b,int r,int c){// printf("%d -- %d (%d) cost %d\n",a,b,r,c); fr[en]=a;to[en]=b;ne[en]=h[a];fl[en]=r;cost[en]=+c;h[a]=en++; fr[en]=b;to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;} int n,m,S=0,T=maxn-1,with[maxn],minn[maxn],vis[maxn],dis[maxn];queue q;int ans; bool tell(){ memset(vis,0,sizeof vis); memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); q.push(S);vis[S]=1;dis[S]=0; while (!q.empty()) { int x=q.front();q.pop();vis[x]=0; for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0) { dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!vis[to[i]]) { vis[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;} int zeng(){// printf("zeng %d\n",dis[T]); for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } return minn[T]*dis[T];} int main(){ memset(h,-1,sizeof h); scanf("%d%d",&n,&m); F(i,1,n) { int x; scanf("%d",&x); add(S,i*2,1,x); add(S,i*2-1,1,0); add(i*2,T,1,0); } F(i,1,m) { int a,b,c; scanf("%d%d%d",&a,&b,&c); if (a>b) swap(a,b); add(a*2-1,b*2,1,c);// add(b*2,a*2-1,1,c); } while (tell()) ans+=zeng(); printf("%d\n",ans);}
【BZOJ 2324】
发现计算1~x的时候只能用1~x中的点经过,所以可以floyd动态加点预处理,然后拆点限制流量,这样就不需要考虑先后顺序,就变成了k路径覆盖的问题了。
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 200005#define maxn 505 int h[maxn],to[maxm],ne[maxm],fr[maxm],fl[maxm],cost[maxm],en=0; void add(int a,int b,int r,int c){// printf("Add %d to %d (%d) cost %d\n",a,b,r,c); fr[en]=a;to[en]=b;fl[en]=r;cost[en]= c;ne[en]=h[a];h[a]=en++; fr[en]=b;to[en]=a;fl[en]=0;cost[en]=-c;ne[en]=h[b];h[b]=en++;} int n,m,k,a[maxn][maxn],S=maxn-2,T=maxn-1,ans;int minn[maxn],with[maxn],dis[maxn],inq[maxn];queue q; bool tell(){ memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); memset(with,0,sizeof with); memset(inq,0,sizeof inq); q.push(S);inq[S]=1;dis[S]=0; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0; for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0){ dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(fl[i],minn[x]); with[to[i]]=i; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;} int zeng(){ for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } return dis[T]*minn[T];} int main(){ memset(h,-1,sizeof h); scanf("%d%d%d",&n,&m,&k); memset(a,0x3f,sizeof a); F(i,0,n) a[i][i]=0; F(i,1,m) { int l,r,w; scanf("%d%d%d",&l,&r,&w); a[l][r]=a[r][l]=min(a[l][r],w); } F(K,0,n)F(i,0,n)F(j,0,n) if (K<=i||K<=j) a[i][j]=min(a[i][j],a[i][K]+a[K][j]); add(S,0,k,0); F(i,1,n)add(S,i,1,0),add(i+n,T,1,0); F(i,0,n-1)F(j,i+1,n) add(i,n+j,inf,a[i][j]); while (tell()) ans+=zeng(); printf("%d\n",ans);}
【CF 277E】
同样拆点限流即可。
#include #include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i<=k;--i)#define inf 0x3f3f3f3f#define maxm 500005#define maxn 2005int h[maxm],to[maxm],fr[maxm],ne[maxm],fl[maxm],en=0,n,maxfl=0;double cost[maxm],dinf;void add(int a,int b,int r,double c){// printf("add %d -- %d (%d) is %f\n",a,b,r,c); fr[en]=a;to[en]=b;fl[en]=r;cost[en]= c;ne[en]=h[a];h[a]=en++; fr[en]=b;to[en]=a;fl[en]=0;cost[en]=-c;ne[en]=h[b];h[b]=en++;}int inq[maxn],minn[maxn],with[maxn],S=maxn-2,T=maxn-1;queue q;int x[maxn],y[maxn];double dis[maxn];bool tell(){ memset(dis,127,sizeof dis); dinf=dis[0]; memset(minn,0x3f,sizeof minn); memset(with,0,sizeof with);// memset(inq,0,sizeof inq); dis[S]=0;q.push(S);inq[S]=1; while (!q.empty()) { int x=q.front();q.pop();inq[x]=0; for (int i=h[x];i>=0;i=ne[i]) if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0){ dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } return dis[T] p[j].y)add(i,j+n,1,dst(p[i],p[j]));} while (tell()) ans+=zeng(); if (maxfl
【BZOJ 2879】
按顺序考虑费用比较难计算,不妨倒着考虑,倒着第k件菜需要k*ai的等待时间,然后这样全部建出来会MLE,所以动态加点即可。
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxn 100005 int h[maxn],to[maxn],ne[maxn],fl[maxn],cost[maxn],en=0; void add(int a,int b,int r,int c){// printf("add %d to %d (%d) cost %d\n",a,b,r,c); to[en]=b;ne[en]=h[a];fl[en]=r;cost[en]= c;h[a]=en++; to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;} int dis[maxn],inq[maxn],minn[maxn],with[maxn],S=maxn-2,T=maxn-1;queue q;int n,m,p[maxn],ned[105][45],cnt,ans,num[maxn],bel[maxn]; bool tell(){ memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); memset(with,0,sizeof with); q.push(S);dis[S]=0;inq[S]=1; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0;// printf("now is %d\n",x); for (int i=h[x];i>=0;i=ne[i]) {// printf("can to %d\n",to[i]); if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0) { dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;} int zeng(){// printf("zeng %d is %d\n",minn[T],dis[T]); int lst; for (int i=T;i!=S;i=to[with[i]^1]) { lst=i; fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; }// printf("firs %d bel is %d\n",lst,bel[lst]); int chef=bel[lst]; ++cnt;num[chef]++; bel[cnt]=chef; add(S,cnt,1,0); F(i,1,n) add(cnt,i+m,1,num[chef]*ned[chef][i]); return minn[T]*dis[T];} int main(){ memset(h,-1,sizeof h); scanf("%d%d",&n,&m); F(i,1,n) scanf("%d",&p[i]); F(i,1,n) F(j,1,m) scanf("%d",&ned[j][i]); F(i,1,m) add(S,i,1,0); F(i,1,n) add(m+i,T,p[i],0); F(i,1,m) { num[i]=1;bel[i]=i; F(j,1,n)add(i,m+j,1,ned[i][j]); } cnt=m+n; while (tell()) ans+=zeng(); printf("%d\n",ans);}
【UVA 1486】
费用不为一次函数,因为货物是整数,直接差分即可,不需要动态建点。
#include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 50005int h[maxm],to[maxm],ne[maxm],fl[maxm],cost[maxm],en=0,ans;void add(int a,int b,int r,int c){// printf("add %d -- %d (%d) cost %d\n",a,b,r,c); to[en]=b;ne[en]=h[a];fl[en]=r;cost[en]= c;h[a]=en++; to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;}int dis[maxm],inq[maxm],with[maxm],minn[maxm],S=maxm-2,T=maxm-1;queue q;int n,m,k,maxfl;bool tell(){ memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); dis[S]=0;q.push(S);inq[S]=1; while (!q.empty()) { int x=q.front(); q.pop();inq[x]=0;// printf("now is %d\n",x); for (int i=h[x];i>=0;i=ne[i]) {// printf("can to %d\n",to[i]);getchar(); if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0) { dis[to[i]]=dis[x]+cost[i]; with[to[i]]=i; minn[to[i]]=min(minn[x],fl[i]); if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;}int zeng(){ for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } maxfl+=minn[T]; return minn[T]*dis[T];}int main(){ while (scanf("%d%d%d",&n,&m,&k)!=EOF) { maxfl=0; memset(h,-1,sizeof h);en=0;ans=0; add(S,1,k,0);add(n,T,k,0); F(i,1,m) { int u,v,a,c; scanf("%d%d%d%d",&u,&v,&a,&c); F(j,1,c) {// printf("now is %d before is %d\n",a*j*j,(a-1)*j*j); int tmp=a*j*j-a*(j-1)*(j-1); add(u,v,1,tmp); } } while (tell()) ans+=zeng(); if (maxfl!=k) printf("-1\n"); else printf("%d\n",ans); }}
【UVA 12092】
要用k条不想交回路覆盖,直接拆点限流,就成了带下界的最小费用可行流。但是发现每个点的入度和出度都为k,那么直接限制出度入度即可。然后跑费用流
#include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 20005#define maxn 505int h[maxn],to[maxm],ne[maxm],fr[maxm],fl[maxm],cost[maxm],en=0; void add(int a,int b,int r,int c){ fr[en]=a;to[en]=b;fl[en]=r;cost[en]= c;ne[en]=h[a];h[a]=en++; fr[en]=b;to[en]=a;fl[en]=0;cost[en]=-c;ne[en]=h[b];h[b]=en++;} int n,m,k,a[maxn][maxn],S=maxn-2,T=maxn-1,ans,mxfl;int minn[maxn],with[maxn],dis[maxn],inq[maxn];queue q; bool tell(){ memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); memset(with,0,sizeof with); memset(inq,0,sizeof inq); q.push(S);inq[S]=1;dis[S]=0; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0; for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0){ dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(fl[i],minn[x]); with[to[i]]=i; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;} int zeng(){ for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } mxfl+=minn[T]; return dis[T]*minn[T];}int x,y,z,tt;int main(){ scanf("%d",&tt); while(tt--) { mxfl=0; memset(h,-1,sizeof h); en=0; scanf("%d%d%d",&n,&m,&k); F(i,1,m) { scanf("%d%d%d",&x,&y,&z); add(2*y,2*x+1,1,z);// add(2*y+1,2*x,1,z); } F(i,0,n-1) { add(S,2*i,k,0); add(2*i+1,T,k,0); } ans=0; while (tell()) ans+=zeng(); if (mxfl!=k*n) printf("-1\n"); else printf("%d\n",ans); }}
【POJ 3155】(吼)
好难的一道题目,首先点集为V,边集为E,然后最大化V/E,不妨二分答案去解这个问题(01分数规划),设d=V/E 即 dE-V=0 发现左侧大于零的时候我们可以把d变大,然后二分,所以我们需要最大化dE-V的值,即最小化V-dE。
首先我们可以套用最大权闭合子图模型,边看做点,两端的点看做条件,然后跑最大权闭合自图即可。
或者根据二元关系建图(来自lydrainbowcat)
然后精度需要卡一卡,尤其最后判断哪些点属于S集合时需要小心。
#include #include #include #include #include #include using namespace std;#define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 1e18#define eps 1e-8#define maxm 20005#define maxn 205int h[maxm],fr[maxm],to[maxm],ne[maxm],en=0,S=maxn-2,T=maxn-1;double fl[maxm];void add(int a,int b,double r){// printf("%d -- %d %f\n",a,b,r); to[en]=b;fr[en]=a;ne[en]=h[a];fl[en]=r;h[a]=en++;} int dis[maxn];queue q; bool tell(){ memset(dis,-1,sizeof dis);dis[S]=0; q.push(S); while (!q.empty()) { int x=q.front(); q.pop();// printf("now is %d\n",x); for (int i=h[x];i>=0;i=ne[i]) {// printf("%d to %d fl is %f\n",x,to[i],fl[i]); if (dis[to[i]]==-1&&fl[i]>eps) { dis[to[i]]=dis[x]+1; q.push(to[i]); } } } return dis[T]!=-1;} double zeng(int k,double now){// printf("zeng %d %f\n",k,now);// getchar(); if (k==T) return now; double r=0; for (int i=h[k];i>=0&&now>r;i=ne[i]) if (dis[to[i]]==dis[k]+1&&fl[i]>eps){ double tmp=zeng(to[i],min(fl[i],now-r)); r+=tmp; fl[i]-=tmp; fl[i^1]+=tmp; } if (r =1.0/n/n) { double mid=(ll+rr)/2; if (solve(mid)>eps) ll=mid; else rr=mid; } if (ll
【BZOJ 1070】
修车?看看这车好修不好修。。。
(去污)大概和美食节一样,不过可以直接建出所有的边然后费用流即可。
#include #include #include #include #include using namespace std; #define F(i,j,k) for (int i=j;i<=k;++i)#define D(i,j,k) for (int i=j;i>=k;--i)#define inf 0x3f3f3f3f#define maxm 100005#define maxn 705 int h[maxm],to[maxm],ne[maxm],fl[maxm],cost[maxm],en=0; void add(int a,int b,int r,int c){// printf("add %d --> %d (%d) cost %d\n",a,b,r,c); to[en]=b;ne[en]=h[a];fl[en]=r;cost[en]= c;h[a]=en++; to[en]=a;ne[en]=h[b];fl[en]=0;cost[en]=-c;h[b]=en++;} queue q;int dis[maxn],inq[maxn],minn[maxn],with[maxn],S=maxn-2,T=maxn-1;int n,m; bool tell(){ memset(dis,0x3f,sizeof dis); memset(minn,0x3f,sizeof minn); while (!q.empty()) q.pop(); q.push(S);dis[S]=0;inq[S]=1; while (!q.empty()) { int x=q.front(); q.pop(); inq[x]=0;// printf("now is %d\n",x); for (int i=h[x];i>=0;i=ne[i]) { if (dis[to[i]]>dis[x]+cost[i]&&fl[i]>0) {// printf("can to %d %d -- %d\n",to[i],dis[to[i]],dis[x]); dis[to[i]]=dis[x]+cost[i]; minn[to[i]]=min(minn[x],fl[i]); with[to[i]]=i; if (!inq[to[i]]) { inq[to[i]]=1; q.push(to[i]); } } } } return dis[T]!=inf;} int zeng(){ for (int i=T;i!=S;i=to[with[i]^1]) { fl[with[i]]-=minn[T]; fl[with[i]^1]+=minn[T]; } return dis[T]*minn[T];} int a[65][65],id[65],cnt=0,ans; int main(){ memset(h,-1,sizeof h); scanf("%d%d",&m,&n); F(i,1,n) F(j,1,m) scanf("%d",&a[j][i]); F(i,1,n) id[i]=++cnt,add(id[i],T,1,0); F(i,1,m) F(j,1,n) { ++cnt; add(S,cnt,1,0); F(k,1,n) add(cnt,id[k],1,j*a[i][k]); } while (tell()) ans+=zeng(); printf("%.2f\n",(ans*1.0)/(n*1.0)); return 0;}