UVA1104 芯片难题 Chips Challenge

题目大意

一个\(n\)\(n\)列的字符矩阵\(S\),每个位置有\(C,.,/\)三种字符,需要往\(.\)中填入尽量多的\(W\)使得,\(\forall i,\sum_{j=1}^n[S_{i,j}=W|S_{i,j}=C]=\sum_{j=1}^n[S_{j,i}=W|S_{j,i}=C]\),设总共填入\(m\)\(W\),还要满足\(\forall i,\sum_{j=1}^n[S_{i,j}=W|S_{i,j}=C]\le m\frac{A}{B}\)

\[n\le40 \]

题解

这种玄学的条件,一看就应该想到网络流
第一个条件,可以把第\(i\)行第\(i\)列,看成一个点,然后用流到\(i\)的流量,和流出\(i\)的流量,来表示第\(i\)行减去第\(i\)列的差,条件是相等,所以我们希望他们的差为\(0\)
\(r_i\)表示行减列的差,将\((i,j)\)填入一个\(W\),会使得,\(r_i++,r_j--\),那么我们就让\(j\)\(i\)连一条流量为\(1\),费用为\(1\)的边
然后初始时,如果\(r_i>0\),就让\(S\)\(i\)连流量为\(r_i\)费用为\(0\)的边,\(r_i<0\)就让\(i\)\(T\)连一条流量为\(-r_i\)的边
第二个条件比较难弄,因为\(m\)会随着填入的\(W\)而变动,由于\(n\)比较小,可以直接枚举\(m\),然后限制就变成了,每一行填入的\(W\)小于等于某个值,这个拆点处理,把\(i\)拆成\(i\)\(i+n\),连一条\(i\)\(i+n\)的边,流量就是\(W\)的上限,前面的连边也要稍改一下
然后就要跑个最大流最大费用
但是仔细思考,发现,它只有\(S\)到别的点的边需要到上限,其他的边也可以通过只流可行流的方式来使费用更大,而且还存在正环问题,题目就变成了,上下界可行流最大费用问题,可以对原图做若干转化来解决
但还有一种更好的方案,就是反过来做,假设\(.\)已经全部填上了\(W\),然后跑最小费用,然后把\(.\)的数量减去最小费用就行,正环问题还有可行流问题就解决了,但是由于第二个条件,每行的\(W\)变成\(.\)的数量有下界,也就是问题就是上下界最大流最小费用,做点转化就解决了
我写完之后一直超时,后来发现可以加个优化,不用每个\(m\)都枚举,可以直接让\(m\)直接跳到费用流的结果,因为\(m\)不会比这个更大,然后就跑的飞快了

代码

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define ll long long
using namespace std;
void read(int &res)
{
	res=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while('0'<=ch&&ch<='9') res=(res<<1)+(res<<3)+(ch^48),ch=getchar();
}
const int N=5e3+100,M=5e4+100,inf=2147483647;
int st[N+10],tot=1;
struct edge
{
	int to,last,flow,val;
}e[M<<1|1];
void add(int a,int b,int c,int d)
{
	e[++tot].to=b;
	e[tot].flow=c;
	e[tot].val=d;
	e[tot].last=st[a];
	st[a]=tot;
}
void Add(int a,int b,int c,int d){add(a,b,c,d),add(b,a,0,-d);}
queue<int> q;int dis[N+10],pre[N+10],pree[N+10],flow[N+10];
bool vis[N+10];
bool spfa(int s,int t)
{
	for(int i=s;i<=t;i++) dis[i]=inf,pre[i]=pree[i]=-1,flow[i]=0;
	vis[s]=true,dis[s]=0,flow[s]=inf,q.push(s);
	for(int u;!q.empty();)
	{
		u=q.front(),q.pop(),vis[u]=false;
		for(int i=st[u],v;i!=0;i=e[i].last)
		{
			v=e[i].to;
			if(e[i].flow&&dis[v]>dis[u]+e[i].val)
			{
				dis[v]=dis[u]+e[i].val,pre[v]=u,pree[v]=i,flow[v]=min(flow[u],e[i].flow);
				if(!vis[v]) q.push(v),vis[v]=true; 
			}
		}
	}
	return pre[t]!=-1;
}
int resf,resc;
void MCMF(int s,int t)
{
	while(spfa(s,t))
	{
		resc+=dis[t]*flow[t],resf+=flow[t];
		int u=t;
		while(u!=s)
		{
			e[pree[u]].flow-=flow[t],e[pree[u]^1].flow+=flow[t];
			u=pre[u];
		}
	}
}
const int mn=50; 
int n,A,B;
char s[mn+1][mn+1];
int row[N+10],col[N+10];
int main()
{
//	freopen("a.in","r",stdin);
	int tt=0;
	while(1)
	{
		tt++; 
		scanf("%d %d %d",&n,&A,&B);
		if(n==0) break;
		for(int i=1;i<=n;i++)
			scanf("%s",s[i]+1),row[i]=col[i]=0;
		int S=0,T=2*n+1,sum=0,sumc=0;
		for(int i=1;i<=n;i++)
			for(int j=1;j<=n;j++)
			{
				if(s[i][j]=='C'||s[i][j]=='.')
					row[i]++,col[j]++;
				if(s[i][j]=='.')
					sum++;
				if(s[i][j]=='C')
					sumc++;
			}
//		for(int i=1;i<=n;i++)
//		{
//			printf("%d %d\n",row[i],col[i]);
//		}
//		printf("sum1 %d\n",sum);
//		printf("sum %d\n",n*n*n*A/B);
		bool flag=false;
		for(int m=sum;m>=0;m--)
		{
//			printf("%d\n",m);
			tot=1;
			for(int i=S;i<=T;i++) st[i]=0;
			int maxflow=0;
			for(int i=1;i<=n;i++)
			{
				if(row[i]-col[i]>0)
					Add(S,i,row[i]-col[i],0),maxflow+=row[i]-col[i];
				else
					Add(i,T,col[i]-row[i],0);
			}
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					if(s[i][j]=='.')
						Add(i+n,j,1,1);
			for(int i=1;i<=n;i++)
			{
				if((m+sumc)*A/B<row[i])
				{
//					puts("fuc");
					Add(S,i+n,row[i]-(m+sumc)*A/B,0);
					Add(i,T,row[i]-(m+sumc)*A/B,0);
					maxflow+=row[i]-(m+sumc)*A/B;
				}
				Add(i,i+n,inf,0);
			}
			resf=resc=0;
			MCMF(S,T);
//			printf("%d %d\n",resf,resc);
			if(resf==maxflow&&sum-resc==m)
			{
				printf("Case %d: %d\n",tt,m);
				flag=true;
				break;
			}
			m=min(sum-resc+1,m);
		}
		if(!flag)
		{
			printf("Case %d: impossible\n",tt);
		}
	}
	return 0;
} 
posted @ 2024-10-12 00:07  _doctorZ  阅读(7)  评论(0编辑  收藏  举报