jzoj5840. Miner(欧拉路径)

题目描述

这里写图片描述
这里写图片描述

样例呢?
我也不知道

task1/2

直接暴力就行了吧。。。

task3

因为m<=15,所以考虑状压dp
设f[t][i][s][0/1/2]表示当前跳跃t次,在第i个点,且走过的边状态为s(状压)时上一个可能的状态

有一个显然的性质,跳跃的次数肯定不会大于m
因为最坏的情况是每一条边跳到其中一个端点,然后再走一次

于是随便转移,时间复杂度 O(m2n2m) O ( m 2 n 2 m ) (最坏情况)
最后反着输出方案

code

#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
using namespace std;

int a[200002][2];
int ls[100001];
int f[16][32768][16][3];
bool bz[16][32768][16];

int p[16]={0,1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384};

int n,m,i,j,k,l,x,y,len,L;

void New(int x,int y)
{
    len++;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;
}

void dg(int x,int y,int z)
{
    if (!bz[f[x][y][z][0]][f[x][y][z][1]][f[x][y][z][2]])
    {
        printf("%d\n",z);
        return;
    }

    dg(f[x][y][z][0],f[x][y][z][1],f[x][y][z][2]);
    printf("%d %d\n",x-f[x][y][z][0],z);
}

int main()
{
    freopen("miner.in","r",stdin);
    freopen("miner.out","w",stdout);

    scanf("%d%d",&n,&m);

    len=1;
    fo(i,1,m)
    {
        scanf("%d%d",&x,&y);

        New(x,y);
        New(y,x);
    }

    if (n<=15 && m<=15)
    {
        L=p[m]*2-1;

        fo(i,1,n)
        bz[0][0][i]=1;

        fo(i,0,m)
        {
            fo(j,0,L-1)
            {
                fo(k,1,n)
                if (bz[i][j][k])
                {
                    for (l=ls[k]; l; l=a[l][1])
                    if (!(j&p[l>>1]))
                    {
                        int J=j|p[l>>1];

                        if (!bz[i][J][a[l][0]])
                        {
                            bz[i][J][a[l][0]]=1;
                            f[i][J][a[l][0]][0]=i;
                            f[i][J][a[l][0]][1]=j;
                            f[i][J][a[l][0]][2]=k;
                        }
                    }

                    if (i<m)
                    {
                        fo(l,1,n)
                        if (!bz[i+1][j][l])
                        {
                            bz[i+1][j][l]=1;
                            f[i+1][j][l][0]=i;
                            f[i+1][j][l][1]=j;
                            f[i+1][j][l][2]=k;
                        }
                    }
                }
            }
        }

        fo(i,0,m)
        {
            fo(j,1,n)
            if (bz[i][L][j])
            {
                printf("%d\n",i);
                dg(i,L,j);

                return 0;
            }
        }

        return 0;
    }
    printf("0\n");//text5

    fclose(stdin);
    fclose(stdout);

    return 0;
}

task4

不知道

task5

因为这里写图片描述这里写图片描述

所以

    printf("0\n");

就有6分了(逃

咳咳
因为没有跳跃操作,所以直接在连通块里找欧拉路径(后来才发现其实不是回路)

欧拉回路/路径

来自百度

如果图G中的一个路径包括每个边恰好一次,则该路径称为欧拉路径(Euler path)。
如果一个回路是欧拉路径,则称为欧拉回路(Euler circuit)。

求欧拉回路有一种十分方便快捷的方法——Hierholzer算法
Hierholzer算法同样资瓷求欧拉路径(前提是要找一个合法的出发点)

设图中度数为奇数的点个数为x个,则
①x=0 任意点都可以
②x=2 在两个奇点中二选一
③x>2 无解


考虑最暴力的方法,每次不断dfs找经过所有边的路径
但是这样效率很低,因为可能会出现这样的情况:
这里写图片描述
从点1出发,沿着1-2-5-6-3-2的顺序去走
这里写图片描述
如果用普通的dfs,则需要退到5处,再走5-1-4-6-3-2,这样效率很低

但欧拉回路(好像)有个神奇的性质,就是当你选对一个点后,只要不走到死路提前去世,随便xjb走都可以走出一条路来

所以观察上面的路径,发现其实5-6-3-2可以放到后面去走,先走5-1-4-5
这里写图片描述

所以程序流程是:

对于当前点u
    for (u,v)∈[E]
        删除 (u,v)
        删除 (v,u)
        递归v
把u加入答案集合

最后倒叙输出答案集合
思想和上面一样,如果走到死胡同就先放着,走完其它的边再走

代码比较简洁就不放了

于是这样就可以过text5了

task6

因为原图可能不连通,所以考虑分开一个个连通块来做
显然做完一个跳到另一个去做才是最优的

所以问题就变成处理每一个连通块
然而连通块内可能不能直接构成一个欧拉路径,必须要用卢本伟修改器来跳


有一个性质,一个无向图内奇数度数的点的个数x一定为偶数
采用反证法反正就是对的
如果x为奇数,则x*奇数度数+y*偶数度数=奇数
但在无向图中,每加一条边总度数就会+2
所以总度数一定为偶数,x必为偶数


因为x可能大于2,所以考虑用一条边把两个奇数度数的点连起来,这样等于做了一次1操作
那么就用最少的边把每个连通块变成x=0 or 2
之后随便搞搞欧拉回路久行了
(要开人工栈)

code

#include <iostream>
#include <cstdlib>
#include <cstdio>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define max(a,b) (a>b?a:b)
using namespace std;

int A[300011];
int a[300011][3];
int ls[100001];
int c[100001];
int b[100001];
bool bz[100001];
int g[100001];
int d[300001];
int D[300001];
bool Bz[300001];
int F[100001];
int fa[100001];
int n,m,i,j,k,l,x,y,len,ans,s,L,Len,II,next,t;

int T[300001];
int I[300001];

void New(int x,int y)
{
    len++;
    A[len]=x;
    a[len][0]=y;
    a[len][1]=ls[x];
    a[ls[x]][2]=len;
    ls[x]=len;
}

int gf(int t)
{
    if (fa[t]==t)
    return t;

    fa[t]=gf(fa[t]);
    return fa[t];
}

void del(int t)
{
    int pre=a[t][1],suc=a[t][2];

    Bz[t]=1;

    if (pre)
    a[pre][2]=suc;
    if (suc)
    a[suc][1]=pre;

    if (ls[A[t]]==t)
    ls[A[t]]=pre;
}

void dfs(int t)
{
    int i;

    F[t]=II;

    if (b[t]&1)
    {
        c[++L]=t;
        s++;
    }

    bz[t]=1;
    for (i=ls[t]; i; i=a[i][1])
    if (!bz[a[i][0]])
    dfs(a[i][0]);
}

void Dfs(int tt)
{
    t=1;
    T[1]=tt;
    while (t)
    {
        if (I[T[t]])
        {
            if (!Bz[I[T[t]]])
            {
                next=a[I[T[t]]][0];

                del(I[T[t]]);
                del(I[T[t]]^1);

                T[++t]=next;
            }
            else
            I[T[t]]=a[I[T[t]]][1];
        }
        else
        {
            d[++Len]=T[t];
            while (g[T[t]])
            d[++Len]=T[t],g[T[t]]--;

            t--;
            I[T[t]]=a[I[T[t]]][1];
        }
    }
}

int main()
{
    freopen("miner.in","r",stdin);
    freopen("miner.out","w",stdout);

    len=1;
    scanf("%d%d",&n,&m);
    fo(i,1,m)
    {
        scanf("%d%d",&x,&y);

        if (x==y)
        g[x]++;
        else
        New(x,y),New(y,x);

        b[x]++;
        b[y]++;
    }

    fo(i,1,n)
    I[i]=ls[i];

    fo(i,1,n) fa[i]=i;

    fo(i,1,n)
    if (!bz[i] && b[i])
    {
        II=i;

        L=0;

        s=0;
        dfs(i);
        s>>=1;

        if (s)
        ans+=(s-1);
        ans++;

        for (j=3; j<=L; j+=2)
        {
            New(c[j],c[j+1]);
            New(c[j+1],c[j]);

            I[c[j]]=ls[c[j]];
            I[c[j+1]]=ls[c[j+1]];

            fa[gf(c[j])]=gf(c[j+1]);
        }

        k=Len+1;

        if (L)
        {
            Dfs(c[1]);

            fo(j,k+1,Len)
            if (d[j-1]!=d[j] && gf(d[j-1])==gf(d[j]))
            {
                fa[d[j-1]]=d[j-1];
                fa[d[j]]=d[j];
                D[j]=1;
            }
        }
        else
        Dfs(i);
    }

    printf("%d\n",ans-1);
    printf("%d\n",d[1]);

    fo(i,2,Len)
    printf("%d %d\n",max(D[i],(F[d[i]]!=F[d[i-1]])),d[i]);

    fclose(stdin);
    fclose(stdout);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值