zoj 2539 Energy Minimization 【构建最小割模型求解 函数最值】【基础题】

Energy Minimization

Time Limit: 2 Seconds       Memory Limit: 65536 KB

Many of the problems that arise in early computer vision can be naturally expressed in terms of minimization of an energy function. Typically, researchers need to rely on general-purpose optimization techniques such as simulated annealing, which is extremely slow in practice. Some functions that have a restricted form can be solved efficiently using subtle algorithms. In this problem your task is to write a program to find the minimal value of a special class of energy functions widely used in image processing.

Suppose an image has R rows and C columns. We can assign each of the pixel a number ranging from 1 to R * C depending on its scan-line order. We define n = R * C and the energy function is in the form of

where
> j in N(i) means that the pixel j is in the left, right, top or bottom neighbor of pixel i;
> the integer pi (0 <= pi <= 255) is the gray level of the pixel i;
> xi (xi in {0, 1}) is the assigned label to the pixel i; and
> the integers v0 and v1 (0 <= v0, v1 <= 255) are the prior estimation of the gray level of the pixels labeled 0 and 1 respectively.

Input Description

Standard input will contain multiple test cases. The first line of the input is a single integer T (1 <= T <= 10) which is the number of test cases. T test cases follow, each preceded by a single blank line.

The first line of each test case contains four integers R, C (2 <= R, C <= 20), v0 and v1. The following R lines contain C integers each, which are the gray level of the pixels. The proper ranges are shown in the problem description.

Output Description

Results should be directed to standard output. Start each case with "Case #:" on a single line, where # is the case number starting from 1. Two consecutive cases should be separated by a single blank line. No blank line should be produced after the last test case.

For each case, output the minimized energy value in a single line.

Sample Input

3

2 2 24 91
236 224
250 248

3 3 144 194
44 33 24
92 4 227
47 63 35

2 4 111 19
65 86 109 153
115 186 146 112

Sample Output

Case 1:
594

Case 2:
893

Case 3:
230


Source: Asia 2005, Hangzhou (Mainland China), Preliminary


如果知道最小割模型可以求解题目函数最值,这道题就很简单了。

碰巧我知道这个应用 ,但找点函数(x-1) * C + y写成(x-1) * R + y WA了一次,真是醉了! o(╯□╰)o


AC这道题后(一定要理解最小割模型的意义),推荐一道比它要难的题(需要先构建函数)点我



题意:给出一个R*C的矩阵,定义n=R*C。

把矩阵转化成数组p[],则矩阵中元素Map[i][j] 在p[]中下标是(i-1) * C+j。

1,xi(1 <= i <=n)的值为0或1;

2,已经给出v0和v1,

3,j 属于N(i)——表示j是i在矩阵中邻近的元素(上、下、左、右)。

现在让你求函数E的最小值。



最小割模型的应用,不多说了。

直接说建图:

设置超级源点S,超级汇点T。我们定义S与x[i]为1的集合相连,x[i]为0的集合和T相连。

1,x[i]选择1(不选0,割去x[i]选0这条边,代价为|p[i] - v1|),i -> T建边,容量为|p[i] - v1|;

2,x[i]选择0(不选1,割去x[i]选1这条边,代价为|p[i] - v0|),S - > i建边,容量为|p[i] - v0|;

3,x[i]选1且x[j]选0(i和j不能在一个集合,割去i和j这条边,代价为|p[i] - p[j]|),i - > j建边,容量为|p[i] - p[j]|。 

4,x[i]选0且x[j]选1,建边同3。

求出的最小割就是函数最小值。



AC代码:


#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#define MAXN 500
#define MAXM 10000
#define INF 0x3f3f3f3f
using namespace std;
struct Edge
{
    int from, to, cap, flow, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int dist[MAXN], cur[MAXN];
bool vis[MAXN];
int Map[21][21];
int R, C, v0, v1;
int getpoint(int x, int y)
{
    return (x-1) * C + y;//犯二了 醉了。。。
}
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v, int w)
{
    Edge E1 = {u, v, w, 0, head[u]};
    edge[edgenum] = E1;
    head[u] = edgenum++;
    Edge E2 = {v, u, 0, 0, head[v]};
    edge[edgenum] = E2;
    head[v] = edgenum++;
}
bool judge(int x, int y)
{
    return x > 0 && x <= R && y > 0 && y <= C;
}
int S, T;//超级源点 超级汇点
int sum;
void getMap()
{
    scanf("%d%d%d%d", &R, &C, &v0, &v1);
    init();
    sum = 0;
    for(int i = 1; i <= R; i++)
    {
        for(int j = 1; j <= C; j++)
            scanf("%d", &Map[i][j]), sum += Map[i][j];
    }
    S = 0, T = getpoint(R, C) + 1;
    int move[2][2] = {0,1, 1,0};
    for(int i = 1; i <= R; i++)
    {
        for(int j = 1; j <= C; j++)
        {
            int u = getpoint(i, j);
            addEdge(u, T, abs(Map[i][j]-v1));//选择1
            addEdge(S, u, abs(Map[i][j]-v0));//选择0
            for(int k = 0; k < 2; k++)//只选取右边两个避免重复 建边
            {
                int x = i + move[k][0];
                int y = j + move[k][1];
                if(!judge(x, y)) continue;//越界
                int v = getpoint(x, y);
                addEdge(u, v, abs(Map[i][j]-Map[x][y]));//选1 -> 选0
                addEdge(v, u, abs(Map[i][j]-Map[x][y]));
            }
        }
    }
}
bool BFS(int s, int t)
{
    queue<int> Q;
    memset(dist, -1, sizeof(dist));
    memset(vis, false, sizeof(vis));
    dist[s] = 0;
    vis[s] = true;
    Q.push(s);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow)
            {
                dist[E.to] = dist[u] + 1;
                if(E.to == t) return true;
                vis[E.to] = true;
                Q.push(E.to);
            }
        }
    }
    return false;
}
int DFS(int x, int a, int t)
{
    if(x == t || a == 0) return a;
    int flow = 0, f;
    for(int &i = cur[x]; i != -1; i = edge[i].next)
    {
        Edge &E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap-E.flow), t)) > 0)
        {
            edge[i].flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}
int Maxflow(int s, int t)
{
    int flow = 0;
    while(BFS(s, t))
    {
        memcpy(cur, head, sizeof(head));
        flow += DFS(s, INF, t);
    }
    return flow;
}
int main()
{
    int t, k = 1;
    scanf("%d", &t);
    while(t--)
    {
        getMap();
        printf("Case %d:\n%d\n", k++, Maxflow(S, T));
        if(t)
            printf("\n");
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值