一些效率上的疑问
查看原帖
一些效率上的疑问
131591
蒟蒻君HJT泽渡透香楼主2022/4/18 12:44

我会在代码后面说一下我的两份代码的区别,所以其实可以不用看下面的代码;

TLE 50 分代码

#include <bits/stdc++.h>
#define debug cout<<"YES"<<endl
using namespace std;
const int inf=1e9;
const int N=81005,M=20000005;
int head[N],ver[M],len[M],nxt[M],fee[M],tot=1,S,T,P=0;
int pv[N],pe[N],n,m,p[45],c[105][45],dis[N],vis[N],mf=0,fl=0;
inline void add_edge(int x,int y,int z,int w){
	++tot,nxt[tot]=head[x],head[x]=tot;
	ver[tot]=y,len[tot]=z,fee[tot]=w;
	++tot,nxt[tot]=head[y],head[y]=tot;
	ver[tot]=x,len[tot]=0,fee[tot]=-w;
	return ;
}
inline int spfa(){
	memset(pv,0,sizeof pv),
	memset(pe,0,sizeof pe),
	memset(dis,0x3f,sizeof dis),
	memset(vis,0,sizeof vis);
	queue<int>q;q.push(S);
	vis[S]=1,dis[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=nxt[i]){
			if(len[i] && dis[ver[i]]>dis[u]+fee[i]){
				dis[ver[i]]=dis[u]+fee[i];
				pv[ver[i]]=u,pe[ver[i]]=i;
				if(!vis[ver[i]]) vis[ver[i]]=1,q.push(ver[i]);
			}
		}
	}
	return pv[T];
}
void EdmondKarps(){
	while(spfa()){
		int dLen=0,flow=inf,now=T;
		while(now!=S){
			dLen+=fee[pe[now]];
			flow=min(flow,len[pe[now]]);
			now=pv[now];
		}
		now=T;
		while(now!=S){
			len[pe[now]]-=flow,
			len[pe[now]^1]+=flow;
			now=pv[now];
		}
		fl+=flow;
		mf=mf+flow*dLen;
		int g=pv[T]%P,k=(pv[T]+P-1)/P;
		if(g) {
			int s=0;
			for(int i=1;i<=n;++i)
				for(int j=1;j<=p[i];++j)
					++s,add_edge(m*P+s,pv[T]+1,1,c[k][i]*(g+1));
		}
	}
	return ;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&p[i]),P+=p[i];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%d",&c[j][i]);
	S=m*P+P+1,T=m*P+P+2;
	int s=0;
	for(int i=1;i<=n;++i) 
		for(int j=1;j<=p[i];++j)
			++s,add_edge(S,m*P+s,1,0);
	for(int i=1;i<=m*P;++i) add_edge(i,T,1,0);
	for(int i=1;i<=m*P;i+=P){
		s=0;
		for(int j=1;j<=n;++j)
			for(int k=1;k<=p[j];++k)
				++s,add_edge(m*P+s,i,1,c[i/P+1][j]);
	} 
	EdmondKarps();
	printf("%d\n",mf);
	return 0;
}

AC 代码 :

#include <bits/stdc++.h>
#define debug cout<<"YES"<<endl
using namespace std;
const int inf=1e9;
const int N=81005,M=15000005;
int head[N],ver[M],len[M],nxt[M],fee[M],tot=1,S,T,P=0;
int pv[N],pe[N],n,m,p[45],c[105][45],dis[N],vis[N],mf=0,fl=0;
inline void add_edge(int x,int y,int z,int w){
	++tot,nxt[tot]=head[x],head[x]=tot;
	ver[tot]=y,len[tot]=z,fee[tot]=w;
	++tot,nxt[tot]=head[y],head[y]=tot;
	ver[tot]=x,len[tot]=0,fee[tot]=-w;
	return ;
}
inline int spfa(){
	memset(pv,0,sizeof pv),
	memset(pe,0,sizeof pe),
	memset(dis,0x3f,sizeof dis),
	memset(vis,0,sizeof vis);
	queue<int>q;q.push(S);
	vis[S]=1,dis[S]=0;
	while(!q.empty()){
		int u=q.front();q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=nxt[i]){
			if(len[i] && dis[ver[i]]>dis[u]+fee[i]){
				dis[ver[i]]=dis[u]+fee[i];
				pv[ver[i]]=u,pe[ver[i]]=i;
				if(!vis[ver[i]]) vis[ver[i]]=1,q.push(ver[i]);
			}
		}
	}
	return pv[T];
}
void EdmondKarps(){
	while(spfa()){
		int dLen=0,flow=inf,now=T;
		while(now!=S){
			dLen+=fee[pe[now]];
			flow=min(flow,len[pe[now]]);
			now=pv[now];
		}
		now=T;
		while(now!=S){
			len[pe[now]]-=flow,
			len[pe[now]^1]+=flow;
			now=pv[now];
		}
		fl+=flow;
		mf=mf+flow*dLen;
		int g=pv[T]%P,k=(pv[T]+P-1)/P;
		if(g) {
			for(int i=1;i<=n;++i)
				add_edge(m*P+i,pv[T]+1,1,c[k][i]*(g+1));
		}
	}
	return ;
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i) scanf("%d",&p[i]),P+=p[i];
	for(int i=1;i<=n;++i)
		for(int j=1;j<=m;++j)
			scanf("%d",&c[j][i]);
	S=m*P+n+1,T=m*P+n+2;
	for(int i=1;i<=n;++i) 
		add_edge(S,m*P+i,p[i],0);
	for(int i=1;i<=m*P;++i) add_edge(i,T,1,0);
	for(int i=1;i<=m*P;i+=P){
		for(int j=1;j<=n;++j)
			add_edge(m*P+j,i,1,c[i/P+1][j]);
	} 
	EdmondKarps();
	printf("%d\n",mf);
	return 0;
}

区别只有 TLE 的代码将一种菜的需要做多份的时候拆成很多个点,其他优化都是一样的。这样写的话点数只比 AC 代码多了最多 800800 个点,而点数的主要负担是厨师做到第 ii 道菜的 mpm\cdot \sum p3200032000 个点。为什么这样就会 TLE 呢?本地运行了一下效率大概差了 1010 倍左右?

2022/4/18 12:44
加载中...