4.22 省选模拟赛 最优价值 网络流 最大权闭合子图

avatar
avatar

这道题涉及了一个很久以前会的知识点 考试的时候建图硬是没想出来 真自闭。

对于n<=10 容易发现可以爆搜全排列 期望的得分20.

对于\(a_i=0\) 观察发现方阵中的值都为正数 这意味着某种数字选多少都行 有关的是当前这种数字选了没有。

数字只有10个 爆搜某种数字选了没有即可。

40 code: 不过值得一提的是多组数据 考试的时候ans没清0 导致爆零。

const int MAXN=110;
int T,n,ans;
char a[MAXN];int b[MAXN][MAXN];
struct wy{int a,b;}w[MAXN];
int vis[MAXN],mark[MAXN];
inline void dfs(int x)
{
	if(x==n+1)
	{
		int cnt=0;
		rep(1,n,i)if(vis[i])++mark[a[i]-'0'];
		rep(0,9,i)if(mark[i])cnt=cnt-((mark[i]-1)*w[i].a+w[i].b);
		rep(1,n,i)
		{
			if(!vis[i])continue;
			rep(1,n,j)if(vis[j])cnt+=b[i][j];
		}
		ans=max(ans,cnt);
		rep(0,9,i)mark[i]=0;
		return;
	}
	vis[x]=1;
	dfs(x+1);
	vis[x]=0;
	dfs(x+1);
}
inline void dfs1(int x)
{
	if(x==10)
	{
		int cnt=0;
		rep(0,9,i)if(vis[i])cnt-=w[i].b;
		rep(1,n,i)
		{
			if(!vis[a[i]-'0'])continue;
			rep(1,n,j)
			{
				if(!vis[a[j]-'0'])continue;
				cnt+=b[i][j];
			}
		}
		ans=max(ans,cnt);
		return;
	}
	vis[x]=1;
	dfs1(x+1);
	vis[x]=0;
	dfs1(x+1);
}
int main()
{
	freopen("value.in","r",stdin);
	freopen("value.out","w",stdout);
	gt(T);
	while(T--)
	{
		gt(n);gc(a);ans=0; 
		int flag1=0;
		rep(0,9,i)
		{
			int x,y;gt(x);gt(y);
			if(x!=flag1)flag1=1;
			w[i]=(wy){x,y};
		}
		rep(1,n,i)rep(1,n,j)
		{
			gt(b[i][j]);
			if(i==j)b[i][j]=0;
		}
		if(n<=10)
		{
			dfs(1);
			put(ans);
			continue;
		}
		if(!flag1)
		{
			dfs1(0);
			put(ans);
			continue;
		}
	}
	return 0;
}

考虑正解。

容易发现 这道题是不能dp的 同时由于n<=100.且求最大值提示算法网络流。

考虑建图 如果把行列建点 那么一些代价很难被体现出来也毫无道理可言。

考虑模拟费用流 可以发现 在一定程度上一个点的退出加入是不满足费用的单调性的 所以费用流无法解决。

考虑最大权闭合子图。容易发现一个点选择了 必然会选择这个点所在的位置 这个点所在位置选择了必然会选择之后的字符位置。

可以发现这是一张闭合无向图。我们尝试把点权加上 对于一类点显然是其原本的价值。对于二类点显然为-a.对于三类点 显然为-b+a.

这样就构成了最大权闭合子图的基本模型。只要从中选出一张子图使其点权和最大即可 同时可以发现满足题目要求。

值得注意的是:

连边的时候要判断权值为正还是为负 为正加起来 跑最小割。

最后总权值-最小割即可。含义:割掉的显然是最小的边权和 要么是不选的 要么是选的 需要花费代价的。

合法性:可以看出这样建图 某个点一旦选择了就付出了相应的代价 所以是合法的。

最优性:正权值和为定值-(要选择的代价+不选择的付出) 由于后者为最小割 所以保证了最小。

const int MAXN=110,maxn=MAXN*MAXN+MAXN;
int TT,n,S,T,cnt,ans,len;
char a[MAXN];
int b[MAXN][MAXN],q[maxn],vis[maxn],cur[maxn];
int s1[MAXN],s2[MAXN],w[MAXN],c[MAXN];
int lin[maxn],nex[MAXN*MAXN<<4],ver[MAXN*MAXN<<4],e[MAXN*MAXN<<4];
inline void add(int x,int y,int z)
{
	ver[++len]=y;nex[len]=lin[x];lin[x]=len;e[len]=z;
	ver[++len]=x;nex[len]=lin[y];lin[y]=len;e[len]=0;
}
inline int bfs()
{
	rep(1,cnt,i)cur[i]=lin[i],vis[i]=0;
	int l=0,r=0;
	q[++r]=S;vis[S]=1;
	while(++l<=r)
	{
		int x=q[l];
		go(x)
		{
			if(vis[tn]||!e[i])continue;
			vis[tn]=vis[x]+1;
			if(tn==T)return 1;
			q[++r]=tn;
		}
	}
	return 0;
}
inline int dinic(int x,int flow)
{
	if(x==T)return flow;
	int res=flow,k;
	for(int i=cur[x];i&&res;i=nex[i])
	{
		cur[x]=i;
		int tn=ver[i];
		if(vis[tn]==vis[x]+1&&e[i])
		{
			k=dinic(tn,min(e[i],res));
			if(!k){vis[tn]=0;continue;}
			res-=k;e[i^1]+=k;e[i]-=k;
		}
	}
	return flow-res;
}
int main()
{
	freopen("1.in","r",stdin);
	gt(TT);
	while(TT--)
	{
		gt(n);gc(a);ans=0;len=1;
		memset(lin,0,sizeof(lin));
		rep(0,9,i)gt(s1[i]),gt(s2[i]);
		rep(1,n,i)rep(1,n,j){gt(b[i][j]);if(i==j)b[i][j]=0;}
		cnt=n*n;S=++cnt;T=++cnt;
		rep(1,n,i)w[i]=++cnt;rep(0,9,i)c[i]=++cnt;
		rep(1,n,i)rep(1,n,j)
		{
			ans+=b[i][j];
			int ww=(i-1)*n+j;
			if(b[i][j])add(S,ww,b[i][j]);
			add(ww,w[i],INF);
			add(ww,w[j],INF);
		}
		rep(1,n,i)
		{
			int cc=a[i]-'0';
			if(s1[cc])add(w[i],T,s1[cc]);
			add(w[i],c[cc],INF);
		}
		rep(0,9,i)if(s1[i]-s2[i]<0)add(c[i],T,-s1[i]+s2[i]);
		int sum=0,flow;
		while(bfs())while((flow=dinic(S,INF)))sum+=flow;
		put(ans-sum);
	}
	return 0;
}
posted @ 2020-04-22 15:11  chdy  阅读(205)  评论(0编辑  收藏  举报