POJ 3422 Kaka's Matrix Travels

做了无数次最大流 这次终于AC了。。。 每次都RE 真是崩溃!

计算得到最多金矿 就是cost为负数 变相求最小费用最大流

3 2
1 2 3
0 0 0
1 4 2

其实他第一次肯定走1->2->5->8->9 得到 9 你可能会疑惑这不就是贪心吗 

这样下去下次最多得到3 但实际上输出是得到4 为啥?

因为走第一次之后他设置了退边的流量为min 使得它可以退回

即1->4->7->8->5->2->3->6->9 这是其中一条路 具体哪条要看进队列顺序

所以其实这样不是贪心  贪心不能做这题的  这题有点动规的意思

 

首先 讲每个点分为两点A,B 然后搭建4条边,一条A->B 费用为金矿数 容量为1的边 这代表这个金矿拿了一次就不能拿了

然后这条边的退变  B->A 费用为-金矿数, 容量为1, 代表每走一次就要把刚才拿到的金矿数吐出来(这是增广路的定义)

第三条是 A->B 费用为0, 容量为k 的边  这代表可以有很多次这条边,但拿不到金矿,有时候虽然这条边没金矿但是可能要借助这条边走到有金矿的地方

第四条就是他的退边。。。

 

建立一个起点s, 终点t。。。  然后求s->t 的最小费用即可。。

因为费用使用了负数。。。故求最小费用的相反数 就是  金矿的最大数!

#include <stdio.h>
#include <string.h>
#include <queue>

using std::queue;

const int MAXN = 55*55*2, INF = 0x3f3f3f3f;
int dis[MAXN], vis[MAXN], pre[MAXN], count[MAXN], head[MAXN];
queue<int> q;
int cnt, tn, k, s, t;
struct node
{
    int u, v, w, c, next;
}e[MAXN*10];
void addEdge(int u, int v, int w, int c) //建立边与它的退边  注意边的++cnt一定要为偶数, 退边的就是紧跟的奇数  e[i] e[i^1]才能相对应起一对正反向边
{
    e[++cnt].u = u;
    e[cnt].v = v;
    e[cnt].w = w;
    e[cnt].c = c;
    e[cnt].next = head[e[cnt].u];
    head[e[cnt].u] = cnt;

    e[++cnt].u = v;
    e[cnt].v = u;
    e[cnt].w = -w;
    e[cnt].c = 0;
    e[cnt].next = head[e[cnt].u];
    head[e[cnt].u] = cnt;

}

int spfa(int src, int des)
{
    int i;
    memset(vis, 0, sizeof(vis));
    memset(count, 0, sizeof(count));
    for(i=0; i<=t; i++)
        dis[i] = INF;
    dis[0] = 0;
    while(!q.empty()) q.pop();
    q.push(src);
    vis[src] = 1;
    pre[src] = -1;
    while(!q.empty())
    {
        int now = q.front();
        q.pop();
        for(i=head[now]; i!=-1; i=e[i].next)
        {
            int v = e[i].v;
            if(e[i].c > 0 && dis[v] > dis[now] + e[i].w)   //若有容量且能松弛操作
            {
                dis[v] = dis[now] + e[i].w;
                pre[v] = i;    //pre数组记录的是边i  不是点!
                if(!vis[v])    //若没在队列就加入, 已在队列的话等他v取出的时候他肯定是多次松弛之后的最优值
                {
                    vis[v] = 1;
                    q.push(v);
                    if(count[v]++ > t)   //存在环
                        return -1;
                }
                
            }
        }
        vis[now] = 0;   //有可能等下还会有对now的松弛操作
    }
    if(dis[des] == INF)
        return -1;
    else
        return 1;
}
int maxFlow(int src, int des)
{
    int i, min = INF, sum = 0;
    while(spfa(src, des) != -1)
    {
        for(i=pre[des]; i!=-1; i=pre[e[i].u])   //算出得到路径的边中容量最小的,则只能使用最小值。。。 这道题里面肯定为1
        {
            min = e[i].c <= min ?e[i].c: min ;
        }
        for(i=pre[des]; i!=-1; i=pre[e[i].u])
        {
            e[i].c -= min ;
            e[i^1].c += min;
            sum += e[i].w;           
        }        
    }
    return sum;
}
int main()
{
    int i, weight;
    cnt = -1;
    scanf("%d %d", &tn, &k);
    s = 0, t = tn*tn*2 + 1;
    for(i=0; i<=t; i++) head[i] = -1;

    addEdge(s, 1, 0, k);  //对起点s建边

    for(i=1; i<=tn*tn; i++)
    {
        scanf("%d", &weight);
        addEdge(i*2-1, i*2, -weight, 1);
        addEdge(i*2-1, i*2, 0, k);
    }
    for(i=1; i<=tn*tn; i++)
    {
        if(i%tn != 0)
            addEdge(i*2, i*2+1, 0, k);
        if(i <= (tn-1)*tn)
            addEdge(i*2, (i+tn)*2-1, 0, k);
    }
    addEdge(t-1, t, 0, k);   //对终点t建边
    printf("%d\n", -maxFlow(s, t));
    return 0;
}

 

posted @ 2013-03-09 20:24  April_Tsui  阅读(161)  评论(0编辑  收藏  举报