UVA1104 芯片难题 Chips Challenge

news/2024/10/12 0:13:37

题目大意

一个\(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];elseAdd(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;
} 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/70461.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

Design Compiler多时钟约束

这里的资料来源于《Synopsys Timing Constraints and Optimization User Guide, Version P-2019.03-SP4, September 2019》 下面图中这几种情况都是我在实际项目中碰到过的,因此有必要单独做个说明。 第一个是同步派生时钟,即CK2是通过CK1的分频来产生的,我们之前的一个实际…

uniapp创建小程序

uniapp创建小程序https://www.dcloud.io/一、安装Hbuilder和对应基本操作 ​ 安装Hbuilder这里就不在赘述。 (一)、插件安装: ​ 如果考虑到后续需要使用Scss,可以前往插件市场进行搜索安装,浏览器会提示我们是否需要打开对应的HbuilderX,然后进入应用进行安装。(二)…

labelme使用方法

labelme是一款在实例分割、语义分割、目标检测等任务中的一个常用工具,本文将介绍如何使用labelme。 labelme有各种版本,包括ubuntu、windows、macOS等。关于windows版本,也可以下载其相关的exe文件https://github.com/wkentaro/labelme/releases来使用标注 一、安装labelme…

《综合与Design Compiler》笔记

《综合与Design Compiler》笔记 一直没系统的整理过DC这块的东西,这里借助一个挺好的文档《综合与Deisgn Compiler》以及我自己的经验和理解来归总一下。 1. 综合是什么 综合是使用软件的方法来设计硬件,然后将门级电路实现与优化的工作留给综合工具的一种设计方法。它是根据…

C# unsafe 快速复制数组

/// <summary>/// 复制内存/// </summary>/// <param name="dest">目标指针位置</param>/// <param name="src">源指针位置</param>/// <param name="count">字节长度</param>/// <returns&…

11.Java集合框架_Set接口

Set接口和常用方法 基本介绍无序(添加和取出的顺序不一致),没有索引。 不允许重复元素,所以最多包含一个null。 JDK API中Set接口的实现类有HashSet、LinkedHashSet和TreeSet。set接口常用方法 和List接口一样,set接口也是Collection的子接口,因此,常用方法和Collection…

罗技鼠标永久宏定义设置

背景 写程序用到最多的组合按键就是ctrl+c, ctrl+v, 而这些能不能在鼠标上实现,这样就能解放左手了(机智如我) 硬件 需要一款支持宏定义的鼠标,而罗技系列正好拥有(未收广告费),目前尝试在g102, g304, gpwer代上都可运行 思路 使用ghub软件定义宏后加载到鼠标的板载内存上遇…