[CEOI1997]参观洞穴 解题报告

22 篇文章 0 订阅

这道题挺有意思的,难点主要在建模。

“对于每个房间来说,我们都能找到一条通往任意一个其他房间的只经过内通道的路线,但是如果我们规定每个内通道只能走一次的话,这样的路线是惟一的。”
也就是说,在删掉外通道以后,所有节点形成了一棵树。
但是我比较傻逼。。我只想到了内节点形成了一棵树(而且是二叉树,但是我不知道这有什么用),所以我以为就是用k条带权树上路径去不重不漏地覆盖整棵树,于是就写了一个O(n^2)的DP。。(结果不知道为什么在POJ上跑的比我O(N)的标算还快)

“从每个房间出发,恰好有三条通道通往另外三个不同的房间。显然,从每个外房间出发的三条通道分别通往它在圆圈上两个相邻的外房间和某个内房间。”
首先,可以发现,如果忽略1号节点,并以与1号节点相邻的内节点(它是一定存在且唯一的)为根的话,所形成的树的所有叶节点都是外节点,所有外节点也都是叶节点。
而且这还是一棵二叉树。

“所有的房间和通道在同一层且任意两条通道不交叉。”这句话的意思是外房间在环上是依据某种DFS序排列的,而且哈密顿序中环上节点的出现序列必然是沿环的。

由于一号节点比较特殊,所以我们考虑枚举一号节点的进出方式之后,将会剩下一坨二叉树。那么我们考虑每个树的根,实际上如果它要被选择的话,它只能被从左子树最右边的孙子和右子树最左边的孙子之间的路径取到——因为这是一棵二叉树。所以路径的选择竟然是唯一的!

所以我们可以先构出二叉树,然后把所有点按dfs序排序,然后处理的时候按dfs序对没被标记的点依次处理。然后还有各种蛋疼的细节,写了我一上午。
Code(O(N!)DFS):

#include<cstdio>
#include<cstring>
int tmp[505];
int ptr[505],next[1505],w[1505],succ[1505],etot=1;
void addedge(int u,int v,int wt){
    next[etot]=ptr[u],ptr[u]=etot,succ[etot]=v,w[etot++]=wt;
}
bool p[505];
int ans=0x7fffffff,n,ansp[505];
void dfs(int node,int nown,int noww,int top){
    if(noww>=ans)return;
    p[node]=1,tmp[top]=node;
    for(int i=ptr[node];i;i=next[i])
        if(!p[succ[i]])dfs(succ[i],nown+1,noww+w[i],top+1);
        else if(succ[i]==1&&nown==n-1){
            ans=noww+w[i];
            memcpy(ansp,tmp,sizeof(int)*n);
        }
    p[node]=0;
}
int main(){
    freopen("cave.in","r",stdin);
    freopen("cave.out","w",stdout);
    int m,k;
    scanf("%d%d",&n,&k);
    m=n+(n>>1);
    int i,u,v,wt;
    for(i=m;i--;){
        scanf("%d%d%d",&u,&v,&wt);
        addedge(u,v,wt),addedge(v,u,wt);
    }
    dfs(1,0,0,0);
    putchar('1');
    for(i=1;i<n;++i)printf(" %d",ansp[i]);
}

Code(O(N^2)DP):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
#include<algorithm>
int next[1505],succ[1505],ptr[505],w[1505],etot=1;
inline void addedge(int u,int v,int wt){
    next[etot]=ptr[u],ptr[u]=etot,w[etot]=wt,succ[etot++]=v;
}
inline void in(int &x){
    char c=getchar();
    while(c<'0'||c>'9')c=getchar();
    x=0;
    for(;c>='0'&&c<='9';c=getchar())x=x*10+(c^'0');
}
int depth[505],k;
inline void depdfs(int node,int ftr){
    depth[node]=depth[ftr]+1;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=ftr&&succ[i]>k)
            depdfs(succ[i],node);
}
int road[505][505],cost[505],nnode;
inline bool finddfs(int node,int x,int ftr){
    road[node][++road[node][0]]=x;
    for(int i=ptr[x];i;i=next[i])
        if(succ[i]!=ftr){
            cost[node]+=w[i];
            //cout<<"Get:"<<succ[i]<<"("<<w[i]<<")\n";
            if(succ[i]==nnode)return 1;
            else if(succ[i]>k&&finddfs(node,succ[i],x))return 1;
            cost[node]-=w[i];
            //cout<<"Del:"<<succ[i]<<"(-"<<w[i]<<")\n";
        }
    --road[node][0];
    return 0;
}
bool p[505];//这个点在不在当前所选的路径上。 
bool flag[505];//外边界的点进不进去。 
int f[505],chosen[505];
#include<vector>
vector<int> belong[505];
#define inf 1000000000
inline int dfs(int node,int ftr){
    if(!p[node])return f[node];
    int ans=0;
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=ftr&&succ[i]>k)
            ans+=dfs(succ[i],node);
    return ans;
}
inline void dpdfs(int node,int ftr){
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=ftr&&succ[i]>k)
            dpdfs(succ[i],node);
    for(int i,j=belong[node].size();j--;){
        for(i=road[belong[node][j]][0];i;--i)p[road[belong[node][j]][i]]=1;
        int tmp=cost[belong[node][j]];
        for(i=ptr[node];i;i=next[i])
            if(succ[i]!=ftr&&succ[i]>k)
                tmp=min(tmp+dfs(succ[i],node),inf);
        if(tmp<f[node]){
            f[node]=tmp;
            chosen[node]=belong[node][j];
            //cout<<node<<":"<<chosen[node]<<"->"<<f[node]<<endl;
        }
        for(i=road[belong[node][j]][0];i;--i)p[road[belong[node][j]][i]]=0;
    }
}
inline void outdfs(int node,int ftr){
    if(!p[node]){
        flag[chosen[node]]=1;
        for(int i=road[chosen[node]][0];i;--i)p[road[chosen[node]][i]]=1;
    }
    for(int i=ptr[node];i;i=next[i])
        if(succ[i]!=ftr&&succ[i]>k)
            outdfs(succ[i],node);
}
int main(){
    freopen("cave.in","r",stdin);
    //freopen("cave_TA.out","w",stdout);
    int n,m;
    in(n),in(k);
    m=3*n>>1;
    int i,j,u,v,wt;
    for(i=m;i--;){
        in(u),in(v),in(wt);
        addedge(u,v,wt),addedge(v,u,wt);
    }
    depdfs(k+1,0);
    int node=1,highest;
    int sum=0,tmp;
    for(i=ptr[1];i;i=next[i])
        if(succ[i]<=k){
            nnode=succ[i];
            cost[node]=-w[i];
            sum+=w[i];
            break;
        }
    do{
        //cout<<"------"<<node<<"----\n";
        for(i=ptr[node];i;i=next[i])
            if(succ[i]>k){
                cost[node]+=w[i];
                finddfs(node,succ[i],0);
                highest=succ[i];
                for(j=road[node][0];j;--j)
                    if(depth[road[node][j]]<depth[highest])
                        highest=road[node][j];
                belong[highest].push_back(node);
                break;
            }
        for(i=ptr[nnode];i;i=next[i]){
            if(succ[i]!=node&&succ[i]<=k){
                node=nnode;
                nnode=succ[i];
                if(node!=1){
                    cost[node]=-w[i];
                    sum+=w[i];
                }
                break;
            }
        }
    }while(node!=1);
    memset(f,60,sizeof(f));
    /*for(i=1;i<=k;++i){
        cout<<i<<":";
        for(j=1;j<=road[i][0];++j)cout<<road[i][j]<<" ";
        cout<<":"<<cost[i]<<endl;
    }
    for(i=k+1;i<=n;++i){
        cout<<i<<":";
        for(j=belong[i].size();j--;)printf("%d ",belong[i][j]);
        puts("");
    }
    cout<<"------------------\n";*/
    dpdfs(k+1,0);
    outdfs(k+1,0);
    //cout<<sum<<"->"<<sum+f[k+1]<<endl;
    for(i=ptr[1];i;i=next[i])
        if(succ[i]<=k){
            nnode=succ[i];
            break;
        }
    putchar('1');
    do{
        if(flag[node])
            for(i=1;i<=road[node][0];++i)
                printf(" %d",road[node][i]);
        for(i=ptr[nnode];i;i=next[i])
            if(succ[i]!=node&&succ[i]<=k){
                node=nnode;
                nnode=succ[i];
                break;
            }
        if(node!=1)printf(" %d",node);
    }while(node!=1);
    //cout<<":"<<sum+f[k+1]<<endl;
}

Code(O(N)):

#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstring>
#include<iostream>
using namespace std;
#include<algorithm>
int n,k;
int ls[505],ldis[505],rs[505],rdis[505],crate[505];
int cnext[505],cdis[505];//圆上下一点。 
int next[1505],succ[1505],w[1505],ptr[505],etot=1;
int sorted[505],stot=1;
inline void addedge(int u,int v,int wt){
    next[etot]=ptr[u],ptr[u]=etot,w[etot]=wt,succ[etot++]=v;
}
inline void build(int node,int ftr){
    if(node>k){
        for(int i=ptr[node];i;i=next[i])
            if(succ[i]!=ftr){
                build(succ[i],node);
                if(ls[node]){
                    rs[node]=succ[i];
                    rdis[node]=w[i];
                }
                else{
                    ls[node]=succ[i];
                    ldis[node]=w[i];
                }
            }
        if(crate[rs[node]]<crate[ls[node]]){
            swap(ls[node],rs[node]);
            swap(ldis[node],rdis[node]);
        }
        crate[node]=crate[ls[node]];
    }
}
inline void dfs(int node){
    sorted[stot++]=node;
    if(node>k)dfs(ls[node]),dfs(rs[node]);
}
bool used[505];
int now,ans=0x7fffffff,road[505],ansroad[505],ansflag;
inline void work(int flag){
    for(int i=1,j;i<n;++i){
        if(!used[sorted[i]])
            if(sorted[i]>k){
                now+=ldis[sorted[i]]+rdis[sorted[i]];
                for(j=ls[sorted[i]];j>k;j=rs[j]){
                    now+=rdis[j];
                    //cout<<j<<" ";
                    used[j]=1;
                }
                //cout<<j<<" ";
                used[j]=1;
                road[j]=sorted[i];
                //cout<<"("<<sorted[i]<<":<-"<<j<<")";
                for(j=rs[sorted[i]];j>k;j=ls[j]){
                    now+=ldis[j];
                    //cout<<j<<" ";
                    used[j]=1;
                }
                //cout<<j<<"\n";
            }
            else{
                //cout<<sorted[i]<<" chooses go out.\n";

                road[sorted[i]]=sorted[i];
                now+=cdis[sorted[i]];
            }
    }
    if(ans>now){
        //cout<<"Get:"<<flag<<endl;
        ans=now,ansflag=flag;
        memcpy(ansroad,road,sizeof(road));
    }
}
inline void rout(int node){
    if(node>k){
        rout(rs[node]);
        printf(" %d",node);
    }
}
int main(){
    freopen("cave.in","r",stdin);
    freopen("cave.out","w",stdout);
    scanf("%d%d",&n,&k);
    int m=(n>>1)+n;
    int u,v,wt;
    while(m--){
        scanf("%d%d%d",&u,&v,&wt);
        addedge(u,v,wt),addedge(v,u,wt);
    }
    int node=1,nnode,ctot=0,i;
    crate[node]=ctot++;
    for(i=ptr[node];i;i=next[i])
        if(succ[i]<=k){
            nnode=succ[i];
            cdis[1]=w[i];
            break;
        }
    cnext[1]=nnode;
    while(nnode!=1)
        for(i=ptr[nnode];i;i=next[i])
            if(succ[i]<=k&&succ[i]!=node){
                cnext[nnode]=succ[i];
                crate[node=nnode]=ctot++;
                cdis[node]=w[i];
                nnode=succ[i];
                break;
            }
    int outnode,outw;
    for(i=ptr[1];i;i=next[i])
        if(succ[i]>k){
            outnode=succ[i];
            outw=w[i];
            build(succ[i],1);
            dfs(succ[i]);
            break;
        }
    //外到1,1到外。
    //puts("-----1-------");
    memset(used,0,sizeof(used));
    now=cdis[sorted[n-1]]+cdis[1];
    used[1]=used[sorted[n-1]]=1;
    work(1);
    //内到1,1到外。
    //puts("------2------");
    memset(used,0,sizeof(used));
    now=cdis[1]+outw;
    used[sorted[n-1]]=used[1]=1;
    for(i=outnode;i>k;i=rs[i]){
        now+=rdis[i];
        used[i]=1;
    }
    work(2);
    //外到1,1到内。 
    //puts("-----3-----");
    memset(used,0,sizeof(used));
    now=cdis[sorted[n-1]]+outw;
    used[1]=used[sorted[n-1]]=1;
    for(i=outnode;i>k;i=ls[i]){
        now+=ldis[i];
        used[i]=1;
    }
    work(3);

    putchar('1');
    if(ansflag==3)
        for(i=outnode;i>k;i=ls[i])
            printf(" %d",i);
    for(node=cnext[1];node!=sorted[n-1];node=cnext[node]){
        printf(" %d",node);
        if(ansroad[node]!=node){
            //cout<<"("<<ansroad[node]<<":"<<ls[ansroad[node]]<<","<<rs[ansroad[node]]<<")";
            rout(ls[ansroad[node]]);
            printf(" %d",ansroad[node]);
            for(i=rs[ansroad[node]];i>k;i=ls[i])printf(" %d",i);
        }
    }
    printf(" %d",node);
    if(ansflag==2)rout(outnode);
    //cout<<":"<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值