hdu 4862 Jump (最小费用流--两种建图方式)

hdu 4862 Jump 

方法一:


用最小K路径覆盖的模板,将每个点拆分为两点表示入和出(标号i,i+N)。源点S向每个i点连边(val = 1, cost = 0),i+N向汇点T连边(val = 1, cost = 0)。若i与j满足题中的关系,则 i 向 j+N连边(val = 1,cost = 点之间移动时能量变化的值)。但每个点入度出度不一定相等(即其可能为起始点),所以加入一个点使其与所有j+N连边(val = 1, cost = 0),且S与其相连(val = k, cost = 0)。若不满流,则说明k值过小以至于有些点没有被访问。
#include<stdio.h>
#include<queue>
using namespace std;
const int MAXN = 500, inf = 0x3f3f3f3f;
struct _edge
{
    int v, next, val, cost;
    _edge(){}
}e[MAXN*MAXN];
int head[MAXN], cnt, fa[MAXN], dis[MAXN], vis[MAXN], pos[MAXN];
int n, m;
inline void add(int u, int v, int val, int c)
{
    e[cnt].v = v; e[cnt].val = val, e[cnt].cost = c; e[cnt].next = head[u];
    head[u] = cnt++;
    e[cnt].v = u; e[cnt].val = 0; e[cnt].cost = -c; e[cnt].next = head[v];
    head[v] = cnt++;
}
bool spfa(int s, int t)
{
    for(int i = 0; i<= t; ++i) fa[i]=-1,dis[i]=inf,vis[i]=0;
    int bg = 0, ed = 0;
    queue<int> q;
    q.push(s);
    dis[s] = 0; vis[s] = 1;
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for (int i = head[u]; ~i; i=e[i].next)
        {
            int v = e[i].v;
            if (e[i].val > 0 && dis[v] > dis[u]+e[i].cost)
            {
                dis[v] = dis[u]+e[i].cost;
                fa[v] = u;
                pos[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return fa[t] != -1;
}
void minCost_maxFlow(int s, int t, int all)
{
    int cost = 0, f = 0;
    while (spfa(s,t))
    {
        for (int i=t; i!=s; i=fa[i])
            e[pos[i]].val--, e[pos[i]^1].val++;
        cost += dis[t];
        ++f;
    }
    if (f != all) printf("-1\n");
    else printf("%d\n", -cost);
}
int mp[15][15];
char str[20];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int k, T, cs = 1, N, s, t, i, j, c, p;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        printf("Case %d : ", cs++);
        N = n*m;
        s = N*2+1; t = N*2+2;
        cnt = 0;
        memset(head, -1, sizeof head);
        for (i = 0; i< n; ++i)
        {
            scanf("%s", str);    
            for (int j = 0; j< m; ++j)
                mp[i][j] = str[j]-'0';
        }
        for (i = 0, p=0; i< n; ++i)
        {
            for (j = 0; j< m; ++j)
            {
                p = i*m+j;
                add(s, p, 1, 0);
                add(2*N, p+N, 1, 0);
                add(p+N, t, 1, 0);
                for (c=1; i+c < n; ++c)
                {
                    if (mp[i+c][j] == mp[i][j]) 
                        add(p, p+m*c+N, 1, -mp[i][j]+c-1);
                    else 
                        add(p, p+c*m+N, 1, c-1);
                }                
                for (c=1; j+c < m; ++c)
                {
                    if (mp[i][j+c] == mp[i][j]) 
                        add(p, p+c+N, 1, -mp[i][j]+c-1);
                    else 
                        add(p, p+c+N, 1, c-1);
                } 
            }
        }
        add(s, N*2, k, 0);
        minCost_maxFlow(s, t, N);
    }
    return 0;
}


方法二:

借用于hdu 4411 相同的思想,因为每个点必须被且仅被访问一次,所以将点拆分为两部分i和i+N,i与i+N连边(val=1,cost =  inf 
或 -inf,取极大或极小按个人习惯),若i和j符合题中关系,则i+N与j连边(val = inf, cost=点之间移动时能量变化的值)。添加辅助点SS,TT。SS向每个 i 连边(val = inf, cost = 0),每个i+N向TT连边(val = inf, cost = 0)。SS向TT连边(val = k, cost = 0)以防止k值过大。最后源点S向SS连边(val=k,cost=0),TT向T连边(val=k,cost=0)。
若k < min(n, m)则说明必定无法照顾到每个点,否则必定可以满流。算出最终的费用后 -inf | +inf * n*m
#include<cstdio>
#include<iostream>
#include<queue>
using namespace std;
const int MAXN = 250, inf = 0x3f3f3f;
struct _edge
{
    int v, next, val, cost;
    _edge(){}
    _edge(int a, int b, int c, int d):v(a),next(b),val(c),cost(d){}
}e[MAXN*MAXN];
int head[MAXN], cnt, fa[MAXN], dis[MAXN], vis[MAXN], pos[MAXN];
int n, m;
inline void add(int u, int v, int val, int c)
{
    e[cnt] = _edge(v, head[u], val, c); head[u] = cnt++;
    e[cnt] = _edge(u, head[v], 0, -c); head[v] = cnt++;
}
bool spfa(int s, int t)
{
    for(int i = 0; i<= t; ++i) fa[i]=-1,dis[i]=-inf,vis[i]=0;
    int bg = 0, ed = 0;
    queue<int> q;
    q.push(s);
    dis[s] = 0; vis[s] = 1;
    while (!q.empty())
    {
        int u = q.front(); q.pop();
        vis[u] = 0;
        for (int i = head[u]; ~i; i=e[i].next)
        {
            int v = e[i].v;
            if (e[i].val > 0 && dis[v] < dis[u]+e[i].cost)
            {
                dis[v] = dis[u]+e[i].cost;
                fa[v] = u;
                pos[v] = i;
                if (!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    return fa[t] != -1;
}
void minCost_maxFlow(int s, int t)
{
    int cost = 0;
    while (spfa(s,t))
    {
    	int mn = inf;  
        for (int i = t; i!= s; i = fa[i])  
        {  
            if (mn > e[pos[i]].val)  
                mn = e[pos[i]].val;  
        }  
        for (int i = t; i!=s; i=fa[i])  
        {  
            e[pos[i]].val -= mn;  
            e[pos[i]^1].val += mn;  
        }
        cost += dis[t]*mn;
    }
    printf("%d\n", cost-n*m*inf);
}
char mp[15][15];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int k, T, cs = 1;
    scanf("%d", &T);
    while(T--)
    {
        scanf("%d%d%d", &n, &m, &k);
        printf("Case %d : ", cs++);
        int N = n*m;
        int ss = N*2, tt = N*2+1;
        int s = tt+1, t = tt+2;
        cnt = 0;
        memset(head, -1, sizeof head);
        for (int i = 0; i< n; ++i)
            scanf("%s", mp[i]);    
        if (k < min(n, m)) 
        {
            printf("-1\n");
            continue;
        }
        for (int i = 0; i< n; ++i)
        {
            for (int j = 0; j< m; ++j)
            {
                int p = i*m+j;
                add(ss, p, inf, 0);
                add(p+N, tt, inf, 0);
                add(p, p+N, 1, inf);
                for (int c=1; i+c < n; ++c)
                if (mp[i+c][j] == mp[i][j]) 
                    add(p+N, p+m*c, inf, mp[i][j]-'0'-c+1);
                else 
                    add(p+N, p+c*m, inf, -c+1);
                for (int c=1; j+c < m; ++c) 
                if (mp[i][j+c] == mp[i][j]) 
                    add(p+N, p+c, inf, mp[i][j]-'0'-c+1);
                else 
                    add(p+N, p+c, inf, -c+1);
            }
        }
        add(ss, tt, k, 0);
        add(s, ss, k, 0);
        add(tt, t, k, 0);
        minCost_maxFlow(s, t);
    }
}


你可能感兴趣的:(最小费用最大流)