HDU Random Maze (欧拉路、费用流)

题意:花最小的费用进行删边操作使图变为欧拉路

题解:模仿混合图欧拉路的构图方法,不过TLE了,看了网上说是SPFA问题,现在还没理解,换了种构图(看其他大牛的博客),

根据a和b的值,在分a>=b 和a<b 两种情况,贪心的策略,考虑保留和取消,在保留的边上对边的度进行调整,删去的边不考虑(因为这个调了很久),终点到起始点连条边,但这条边不加到网络里(因为它是虚拟固定的,不需要调整),网络流跑出来的就是最小的费用把原图调整为欧拉路的方法。

 

 

Geners296MS292K3845BC++2011-10-18 19:02:30

 

 

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;
const int maxn=123;
const int maxm=100000;
const int inf=0x5fffffff;

struct Edge {
    int v,w,c,next;
}edge[maxm];
int cnt, head[maxn];
void addedge(int u, int v, int w, int c)
{
    edge[cnt].v=v;
    edge[cnt].w=w;
    edge[cnt].c=c;
    edge[cnt].next=head[u];
    head[u]=cnt++;
    edge[cnt].v=u;
    edge[cnt].w=0;
    edge[cnt].c=-c;
    edge[cnt].next=head[v];
    head[v]=cnt++;
}

int dis[maxn],pre[maxn];//最小费用和前驱结点
int alpha[maxn];//标记
int que[maxn],qhead,qrear;
int maxf;
int spfa (int s,int e)//源汇点
{
    //寻找费用增广,没有返回-1;
    for (int i=0 ; i<maxn ; ++i)
        dis[i]=inf;
    memset (alpha , 0 , sizeof(alpha));
    dis[s]=0;
    que[qhead=0]=s;
    qrear=1;
    alpha[s]=1;
    while (qhead!=qrear)//头可能大于尾
    {
        //puts("!!");
        //printf("%d %d\n",qhead, qrear);
        //system("pause");
        int k=que[qhead++];
        qhead%=maxn;//循环队列
        alpha[k]=0;
        for (int q=head[k] ; ~q ; q=edge[q].next)
            if(edge[q].w)
                if(dis[k]+edge[q].c<dis[edge[q].v])
                {
                    dis[edge[q].v]=dis[k]+edge[q].c;
                    //printf("%d\n", edge[q].c);
                    pre[edge[q].v]=q;
                    if(!alpha[edge[q].v])
                    {
                        alpha[edge[q].v]=true;
                        if(edge[q].c<0)
                        {
                            qhead=(qhead-1+maxn)%maxn;
                            que[qhead]=edge[q].v;
                        }
                        else
                        {
                            que[qrear++]=edge[q].v;
                            qrear%=maxn;
                        }
                    }
                }
    }
    if(dis[e]==inf)return -1;
    //终点不可达,返回-1;
    int k=inf;
    for(int i=e ; i!=s ; i=edge[pre[i]^1].v)
        k=min(k,edge[pre[i]].w);
    maxf+=k;//sum记录最大流(有些题里会用到)
    return k;//返回该可行流流量
}

int mcmf(int s,int t)
{
    int ans=0,k;
    while (~(k=spfa(s,t)))
    {
        for (int i=t ; i!=s ; i=edge[pre[i]^1].v)
        {
            edge[pre[i]].w-=k;
            edge[pre[i]^1].w+=k;
        }//更新流
        ans+=dis[t]*k;//最小费用*流量
    }
    return ans;
}

void init()
{
        memset (head, -1, sizeof(head));
        cnt=0;
}

int deg[maxn];
int n, m, s, t;
int u, v, a, b;
int sum, ans;
int main ()
{
    int cas;
    //freopen ("out.txt", "r", stdin);
    scanf("%d",&cas);
    for (int I=1 ; I<=cas ; ++I)
    {
        memset (deg, 0, sizeof(deg));
        init();
        maxf=sum=ans=0;
        scanf("%d%d%d%d", &n, &m, &s, &t);
        ++deg[s]; --deg[t];
        for(int i=0 ; i<m ; ++i)
        {
            scanf("%d%d%d%d", &u, &v, &a, &b);
            if(a>b)
            {
                //++deg[u]; --deg[v];
                ans+=b;
                addedge(u, v, 1, a-b);
            }
            else
            {
                ++deg[v]; --deg[u];
                ans+=a;
                addedge(v, u, 1, b-a);
            }
            //printf("%d %d %d\n", u, v, 1);
        }
        //++deg[s]; --deg[t];
        for (int i=1 ; i<=n ; ++i)
        {
            if(deg[i]>0)addedge(0, i, deg[i], 0),sum+=deg[i];
            //printf("%d %d %d\n", 0, i, deg[i]);
            if(deg[i]<0)addedge(i, n+1, -deg[i], 0);
            //printf("%d %d %d\n", i, n+1, -deg[i]);
        }
        //printf("total=%d  ", ans);
        ans+=mcmf(0, n+1);
        for (int i=1 ; i<=n ; ++i)
            printf("%d ",deg[i]);
        printf("\n");
        printf("%d %d\n", n, m);
        //printf("%d  %d  %d\n", ans, maxf, sum);
        if(maxf==sum)printf("Case %d: %d\n", I, ans);
        else printf("Case %d: impossible\n",I);
    }
    return 0;
}


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值