JZOJ1094【SDOI2009】最优图像

Description:

小E 在好友小W 的家中发现一幅神奇的图画,对此颇有兴趣。它可以被看做一个包含
N×M 个像素的黑白图像,为了方便起见,我们用0 表示白色像素,1 表示黑色像素。小E
认为这幅图画暗藏玄机,因此他记录下了这幅图像中每行、每列的黑色像素数量,以回去慢
慢研究其中的奥妙。
有一天,小W 不慎将图画打湿,原本的图像已经很难分辨。他十分着急,于是找来小
E,希望共同还原这幅图画。根据打湿后的图画,他们无法确定真正的图像,然而可以推测
出每个像素原本是黑色像素的概率Pij%。那么,一个完整的图像的出现概率就可以定义为:
这里写图片描述
其中Sij表示在还原后的图像中,像素是白色(0)还是黑色(1)。换句话说,一个完整图像出
现概率就等于其所有黑色像素的出现概率之积。显然,图像的黑色像素不能包含概率为0 的像素。
然而,小E对此也无能为力。因此他们找到了会编程的小F,也就是你,请你根据以上
信息,告诉他们最有可能是原始图像的答案是什么。

Input:

输入文件的第一行是两个正整数N和M,表示图像大小。
接下来N行每行包含M 个整数,表示每个像素是黑色像素的概率为Pij%。0 ≤ Pij < 100。
接下来一行有N个非负整数,表示每一行中黑色像素的个数。
接下来一行有M 个非负整数,表示每一列中黑色像素的个数。]

Output:

输出文件包含一个N×M的01 矩阵,表示你还原出的图像。输出不包含空格。
图像每行、每列中1的个数必须与输入一致,且是所有可能的图像中出现概率最大的一个。
输入数据保证至少存在一个可能的图像。如果有多种最优图像,任意输出一种即可。

Sample Input:

输入文件的第一行是两个正整数N和M,表示图像大小。
接下来N行每行包含M 个整数,表示每个像素是黑色像素的概率为Pij%。0 ≤ Pij < 100。
接下来一行有N个非负整数,表示每一行中黑色像素的个数。
接下来一行有M 个非负整数,表示每一列中黑色像素的个数。

Sample Output:

10
01

Data Constraint:

对于20%的数据,N , M ≤ 5;
对于100%的数据,N , M ≤ 100。

Hint:

共有两种可能的图像:
01
10

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

题目大意:

有n*m个点,每个点染色为黑点有一个概率。
现在给出条件,要求每一行、每一列要有多少个黑点。
求满足条件的最大概率的染色方案。

题解:

这题其实并不难。
概率是要相乘的,我们把它们转成某个数为底的对数,就可以变成加法。
建n个x点,S到它们的流量为它们这一行所需要的黑点数,费用为0。
建m个y点,它们到T的流量为它们这一列所需要的黑点数。
x到y连一条流量为1,费用为概率的对数。
求最大流量最大费用。
然后判断x->y的弧的流量是否流完了,输出答案。

Code:

#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#define abs(a) ((a) < 0 ? -(a) : (a))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;

const int Maxn = 205, INF = 1000000000, INF2 = 100000;
const double wu = 0.000000001;

int n, m, x, y;
int final[Maxn * 3], tot = 1, S, T;
struct edge {
    int next, to, r; double w;
}e[Maxn * Maxn * 3];
int bz[Maxn * 3];
double dis[Maxn * 3];
int bx[Maxn][Maxn];

void link(int x, int y, int r, double w) {
    e[++ tot].next = final[x], e[tot].to = y, e[tot].r = r, e[tot].w = w, final[x] = tot;
    e[++ tot].next = final[y], e[tot].to = x, e[tot].r = 0, e[tot].w = -w, final[y] = tot;
}

void Init() {
    scanf("%d %d", &n, &m);
    S = n + m + 1, T = S + 1;

    fo(i, 1, n) fo(j, 1, m) {
        scanf("%d", &x);
        if(x != 0) link(i, n + j, 1, INF2 - log2(x));
    }
    fo(i, 1, n) {
        scanf("%d", &x);
        link(S, i, x, INF2);
    }
    fo(i, 1, m) {
        scanf("%d", &x);
        link(i + n, T, x, INF2);
    }
}

int aug(int x, int flow) {
    if(x == T) return flow;
    bz[x] = 1;
    int use = 0;
    for(int i = final[x]; i; i = e[i].next) {
        int y = e[i].to;
        if(!bz[y] && e[i].r && abs(dis[y] + e[i].w - dis[x]) < wu) {
            int tmp = aug(y, min(e[i].r, flow - use));
            e[i].r -= tmp; e[i ^ 1].r += tmp; use += tmp;
            if(use == flow) return use;
        }
    }
    return use;
}

bool change() {
    double minh = INF;
    fo(i, 1, T) if(bz[i]) {
        for(int k = final[i]; k; k = e[k].next) if(!bz[e[k].to] && e[k].r) {
            minh = min(minh, dis[e[k].to] + e[k].w - dis[i]);
        }
    }
    if(minh < -1) return 0;
    if(abs(minh - INF) < wu) return 0;
    fo(i, 1, T) if(bz[i])
        dis[i] += minh;
    return 1;
}

void End() {
    fo(i, 1, n)
        for(int k = final[i]; k; k = e[k].next)
            if(e[k].r == 0) bx[i][e[k].to - n] = 1;
    fo(i, 1, n) {
        fo(j, 1, m) printf("%d", bx[i][j]);
        printf("\n");
    }
}

int main() {
    Init();
    do do memset(bz, 0, sizeof(bz));
    while(aug(S, INF));
    while(change());
    End();
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值