POJ 3686 最小费用最大流(拆点建图)

来源:互联网 发布:软件杂志高岚 编辑:程序博客网 时间:2024/06/04 18:54

思路:这题还挺难的。刚开始看错题目意思了,然后建图错了得不出答案,然后看了下别人的题解,原来每个矩阵的点还在拆成n个点才得。

然后昨晚看了1个小时多小时没理解,今早过来再看,然后用别人的代码运行了一下我自己想出的样例,然后才慢慢理解。

解题分析:《参考博客:http://blog.csdn.net/weiguang_123/article/details/7881799》

 假设某个机器处理了k个玩具,那么对于这些玩具,有两种时间,一种是真正处理的时间,一种是等待的时间,等待的时间就是之前所有处理的玩具的时间,

假设这k个玩具真正用在加工的时间分为a1,a2,a3...ak, 那么每个玩具实际的时间是加工的时间+等待时间,分别为a1, a1+a2, a1+a2+a3.......a1+a2+...ak。求和之后变为 a1 *k + a2 * (k - 1) + a3 * (k - 2).... + ak

       这时就发现,每个玩具之间的实际时间可以分开来算 然后求和了。

       因为对每个机器,最多可以处理n个玩具,所以可以拆成n个点,1~n分别代表某个玩具在这个机器上倒数第几个被加工的, 所以我们对于每个玩具i,机器j中拆的每个点k,连接一条z[i][j]*k权值的边。

      建图:建立源汇点,源点和每个订单流量为1,费用为0;订单和每台机器拆成的点k建立流量为1,费用为Mij*k;

k与汇点建立流量为1,费用为0,求最小费用最大流即可。神吧这建图模型。


我自己想的样例是:

3  4

100 100 100 1
99 99 99 2
98 98 98 3

按口算应该是:1+(1+2)+(1+2+3)=10,按上面解题分析变成:1*3+2*2+3=10,所以1,2,3这个点被分解成了三个点了,1分解成:1,1*2,1*3;2分解成:2,2*2,2*3;3分解成:3,3*2,3*3。所以可以看出这里取的最佳路径为:1的点取1*3,2的点取2*2,3的点取3,意思即:在第4个机器上3先加工,然后第二个加工的是2,第三个加工的是1,所以答案为10与口算的一样,这样就可以建图用最小费用最大流求了。

#pragma comment(linker, "/STACK:1024000000,1024000000")#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<map>#include<queue>#include<set>#include<cmath>#include<bitset>#define mem(a,b) memset(a,b,sizeof(a))#define lson i<<1,l,mid#define rson i<<1|1,mid+1,r#define llson j<<1,l,mid#define rrson j<<1|1,mid+1,r#define INF 0x7ffffffftypedef long long ll;typedef unsigned long long ull;using namespace std;#define maxn 300005struct{    int v,w,c,next,re;    //re记录逆边的下标,c是费用,w是流量} e[maxn];int n,cnt;int head[maxn],que[maxn*8],pre[maxn],dis[maxn];bool vis[maxn];void add(int u, int v, int w, int c){    e[cnt].v=v,e[cnt].w=w,e[cnt].c=c;    e[cnt].next=head[u];    e[cnt].re=cnt+1,head[u]=cnt++;    e[cnt].v=u,e[cnt].w=0,e[cnt].c=-c;    e[cnt].next=head[v];    e[cnt].re=cnt-1,head[v]=cnt++;}bool spfa(){    int i, l = 0, r = 1;    for(i = 0; i <= n; i ++)        dis[i] = INF,vis[i] = false;    dis[0]=0,que[0]=0,vis[0]=true;    while(l<r)    {        int u=que[l++];        for(i=head[u];i!=-1;i=e[i].next)        {            int v = e[i].v;            if(e[i].w&&dis[v]>dis[u]+e[i].c)            {                dis[v] = dis[u] + e[i].c;                pre[v] = i;                if(!vis[v])                {                    vis[v] = true;                    que[r++] = v;                }            }        }        vis[u] = false;    }    return dis[n]!=INF;}int change(){    int i,p,sum=INF,ans=0;    for(i=n;i!=0;i=e[e[p].re].v)    {//e[e[p].re].v为前向结点,不理解看add和spfa        p=pre[i];//p为前向结点编号        sum=min(sum,e[p].w);    }    for(i=n;i!=0;i=e[e[p].re].v)    {        p=pre[i];        e[p].w-=sum;        e[e[p].re].w+=sum;        ans+=sum*e[p].c;//c记录的为单位流量费用,必须得乘以流量。    }    return ans;}int EK(){    int sum=0;    while(spfa()) sum+=change();    return sum;}void init(){    mem(head,-1),mem(pre,0),cnt=0;}int a[51][51];int main(){    //freopen("1.txt","r",stdin);    int t;    scanf("%d",&t);    while(t--)    {        int N,M,i,j,k;        init();        scanf("%d%d",&N,&M);        for(i=1;i<=N;i++)            for(j=1;j<=M;j++)                scanf("%d",&a[i][j]);        for(i=1;i<=N;i++)            add(0,i,1,0);        for(i=1;i<=N;i++)            for(j=1;j<=M;j++)                for(k=1;k<=N;k++)                    add(i,N+(j-1)*N+k,1,a[i][j]*k);        for(j=1;j<=M;j++)            for(k=1;k<=N;k++)                add(N+(j-1)*N+k,N+N*M+1,1,0);        n=N+N*M+1;        printf("%.6f\n",EK()*1.0/N);    }    return 0;}


0 0
原创粉丝点击