zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    正则表达式匹配负数和数字
    下拉框select chosen被遮盖
    获取JavaScript对象的方法
    管理机--Jumpserver由docker搭建
    腾讯云--腾讯云sdk-实现脚本修改腾讯云负载均衡权重
    Linux系统中使用confluence构建企业wiki
    腾讯云--对象存储cos绑定自定义域名
    python(一)python的操作符
    pytest(五)用例传fixture参数
    pytest(四)firture自定义用例预置条件
  • 原文地址:https://www.cnblogs.com/chdy/p/12752213.html
Copyright © 2011-2022 走看看