P2410 [SDOI2009]最优图像 乘法费用流

题目

P2410 [SDOI2009]最优图像(https://www.luogu.com.cn/problem/P2410)

题目背景

小 E 在好友小 W 的家中发现一幅神奇的图画,对此颇有兴趣。

题目描述

这幅画可以被看做一个包含 n×m 个像素的黑白图像,为了方便起见,我们用 0 表示白色像素,1 表示黑色像素。小 E 认为这幅图画暗藏玄机,因此他记录下了这幅图像中每行、每列的黑色像素数量,以回去慢慢研究其中的奥妙。

有一天,小 W 不慎将图画打湿,原本的图像已经很难分辨。他十分着急,于是找来小 E,希望共同还原这幅图画。根据打湿后的图画,他们无法确定真正的图像,然而可以推测出每个像素原本是黑色像素的概率pij%

%。那么,一个完整的图像的出现概率就可以定义为:

其中 si,j 表示在还原后的图像中,像素是白色(0)还是黑色(1),[si,j=1] 表示若 si,j=1 ,则该表达式的值为 1,否则为 0。

换句话说,一个完整图像出现概率就等于其所有黑色像素的出现概率之积。显然,图像的黑色像素不能包含概率为 0 的像素。

然而,小 E 对此也无能为力。因此他们找到了会编程的小 F,也就是你,请你根据以上信息,告诉他们最有可能是原始图像的答案是什么。

输入格式

第一行是两个整数 n, m,表示图像大小。

第 2 到第 (n + 1)行,每行 m 个整数,第 (i + 1)行的第 j 个整数 pi,j

表示第 i 行第 j 列的像素是黑色的概率。

接下来一行有 n 个整数,第 i 个整数 ai表示第 i 行中黑色像素的个数。

接下来一行有 m 个整数,第 i 个整数 bi 表示第 i 列中黑色像素的个数。

输出格式

本题存在 Special Judge。

输出 n 行每行一个长度为 m 的只含字符 0 和字符 1 的字符串,表示答案。

输入数据保证至少存在一个可能的图像。如果有多种最优图像,任意输出一种即可。

输入

2 2
90 10
20 80
1 1
1 1

输出

10
01
说明/提示
样例输入输出 1 解释
共有两种可能的图像:

01
10
10
01
前者的出现概率是 0.1×0.2=0.02,后者的出现概率是 0.9×0.8=0.72,故后者是最优图像。

数据规模与约定
对于 20% 的数据,保证 n,m≤5。
对于 100% 的数据,保证 1≤n,m≤100,0pi,j100,0≤a_i≤m,0≤b_i≤n。

思路:

标准的费用流,每行和每列作为点。但是这里的费用是概率,并且是相乘。那么spfa改成最长路。
+法改为x,-法改为/。并且反向边的费用为倒数,不是负数。

#include<bits/stdc++.h>
using namespace std;
//x->y incf w
const int inf=0x3f3f3f3f;
const double eps=1e-6;

int cnt=1,n,m,s,t;
double dis[405],w[200005];
int a[105],b[105],vis[405],val[105][105],mp[105][105];
int h[405],to[200005],ver[200005],incf[200005],cur[405];
inline int read() {
	int x=0,f=1;char s=getchar();
	while(s>'9'||s<'0') {if(s=='-') f=-1;s=getchar();}
	while(s>='0'&&s<='9') {x=x*10+s-'0';s=getchar();}
	return x*f;
}
inline void add(int x,int y,int z,double v) {
	to[++cnt]=y;
	ver[cnt]=h[x];
	incf[cnt]=z;
	w[cnt]=v;
	h[x]=cnt;
}
inline void add_d(int x,int y,int z,double v) {add(x,y,z,v);add(y,x,0,1.0/v);}
inline bool spfa() {
	memset(dis, 0, sizeof dis);
	memset(vis, false, sizeof vis);
	queue<int> Q;
	dis[t]=1.00;Q.push(t);
	while(Q.size()) {
		int x=Q.front();Q.pop();
		vis[x]=0;
		for(int i=h[x];i;i=ver[i]) {
			int y=to[i];
			if((dis[y]<dis[x]*w[i^1])&&(incf[i^1])) {
				dis[y]=dis[x]*w[i^1];
				if(!vis[y]) {vis[y]=1;Q.push(y);}
			}
		}
	}
	return dis[s]>0;
}
int dfs(int x,int flow) {
	if(x==t) return flow;
	vis[x]=1;
	int rest=flow;
	for(int i=h[x];i&&rest;i=ver[i]) {
		int y=to[i];
		if(!vis[y]&&(fabs(dis[x]-dis[y]*w[i])<=eps)&&incf[i]) {
			int k = dfs(y,min(rest,incf[i]));
			rest-=k;incf[i]-=k;incf[i^1]+=k;
		}
	}
	vis[x]=0;
	return flow-rest;
}
int main() {
	n=read(),m=read();
	int maxflow=0;
	s=n+m+1;t=n+m+2;
	for(int i=1;i<=n;++i) {
        for(int j=1;j<=m;++j) {
            int x = read();
            if (x) add_d(i,j+n,1,x*0.01);
        }
    }
	for(int i=1;i<=n;++i) {
        int x = read();
        add_d(s,i,x,1.0);
	}
	for(int i=1;i<=m;++i) {
        int x = read();
        add_d(i+n,t,x,1.0);
	}
	while(spfa()) {
//        memcpy(cur, h, sizeof h);
//        dfs(s,inf);
        while(dfs(s, INT_MAX)) memset(vis, false, sizeof vis);
    }
	for(int x=1;x<=n;++x) {
		for(int i=h[x];i;i=ver[i]) {
			int y=to[i];
			if(y==s) continue;
			if(!incf[i]) mp[x][y-n]=1;
		}
	}
	for(int x=1;x<=n;++x) {
		for(int y=1;y<=m;++y) {
			printf("%d",mp[x][y]);
		}
		printf("\n");
	}
	return 0;
}
posted @   liweihang  阅读(102)  评论(0编辑  收藏  举报
编辑推荐:
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
Live2D
欢迎阅读『P2410 [SDOI2009]最优图像 乘法费用流』
点击右上角即可分享
微信分享提示