/ Randle / 题库 /

香子兰 T3

香子兰 T3

题目描述
你承包了一片香子兰花田。现在到了收获的季节,你需要把种下的香子兰全部收获起来,到花店卖掉并取得新的种子,再向田里播种下一季的香子兰。
你的花田一共由n-2片花田组成,编号从1到n-2。算上你的家和花店,一共有n个地点,其中你的家编号为0,花店编号为n-1。即,家、花田、花店都属于地点,且它们都有一个唯一的0~n-1的编号。有m条双向道路连接这些地点。保证所有地点间都是直接或间接连通的。
你需要从家里出发,经过所有的花田进行收获,再到达花店,再从花店出发经过所有花田进行播种,最后重新回到家中。当你经过一片花田的时候,你可以选择收获、播种或者什么事都不做,也就是说你经过一片未收割的花田时可以不立即收割它,播种亦然。然而,播种必须发生在你完成了所有收获并到花店交货之后。在完成最后一个花田的收获后,你必须在到达花店后才能开始播种。也就是说,在你没有收获完所有花田并到花店交货前,即使你已经经过了花店,你也不能进行播种。(啰嗦了这么多但愿讲明白了)
然而还有一个问题。在收割完花朵后,花田会变得光秃秃的,此时土地里的水分会迅速蒸发。考虑到这个问题,更早被收割的花田也理应更早地被播种。具体来说,你必须保证前一半个被收割的花田也是前一半个被播种的,向下取整。你不需要保证这些花田收割和播种的顺序完全一致,而只需要保证前一半名的集合不变即可。
现在,你需要求出完成上述一系列动作走过的最短路程。

输入格式
第一行包含两个整数n, m,表示地点总数和道路条数;
接下来m行,每行包含3个整数xi, yi, zi,表示有一条连接编号为xi和yi 的地点,长为zi的道路。

输出格式
一行,包含一个整数,表示最短路程。

样例输入

4 4
0 1 1
1 2 2
2 3 1
1 3 1

样例输出

10

样例解释
从0出发,先走到1收获(距离1),再走到2收获(距离2),再走到3交货(距离1),再走到1播种(距离1),再走到2播种(距离2),再走到1什么都不干(距离2),再走到0(距离1)。总距离为10。前一半个被收获和播种的花田集合是{1号花田}。

数据范围
对于30%的数据,有n≤8;
另有10%的数据,有m=n-1,且对1≤i≤m,保证xi=i-1,yi=i;
另有20%的数据,有m=n-1,且每个花田至多只与另外两个花田有道路相连;
对于100%的数据,有n≤20,m≤400,0≤xi, yi≤n-1,xi≠yi,0≤zi≤10000。

std

#include <cstdio>
#define inf 1000000007
#define N 24
int a[N][N],d[N][N],f[2][N][1050000],e[N],cnt[1050000],n,n1,n2,x,y,z,i,j,m,k,q,ans,sta;
int main()
{
    freopen("vanilla.in", "r", stdin);
    freopen("vanilla.out", "w", stdout);
    e[0]=1;
    //预处理2^i
    for(i=1;i<=22;++i)
        e[i]=e[i-1]<<1;
    //预处理每个二进制数中有几个1
    for(i=0;i<e[20];++i)
        for(x=i;x!=0;x>>=1)
            cnt[i]+=x&1;

    scanf("%d%d",&n,&m);
    for(i=1;i<=n;++i)
    for(j=1;j<=n;++j)
        d[i][j]=inf*(i!=j);
    for(i=1;i<=m;++i)
    {
        scanf("%d%d%d",&x,&y,&z);
        ++x,++y;
        if(z<d[x][y]) 
            d[x][y]=d[y][x]=z;
    }
    // floyd求两两最短路
    for(k=1;k<=n;++k)
        for(i=1;i<=n;++i)
            for(j=1;j<=n;++j)
                if(d[i][k]+d[k][j]<d[i][j])
                    d[i][j]=d[i][k]+d[k][j];
    if(n==3)
    {
        printf("%d\n",(d[1][2]+d[2][3])*2);
        return 0;
    }
    n1=(n-2)/2;
    n2=n-2-n1;
    //求从家、花店开始,走到点i,经过的点为j的最短路
    //q=0:从家开始,q=1:从花店开始
    for(q=0;q<=1;++q)
    {
        //初始化状态
        for(i=1;i<=n;++i)
        for(j=0;j<e[n-2];++j)
            f[q][i][j]=inf;
        if(q==0)
        {
            for(i=2;i<n;++i)
                f[q][i][e[i-2]]=d[1][i];
        }
        else
        {
            for(i=2;i<n;++i)
                f[q][i][e[i-2]]=d[n][i];
        }
        //dp
        for(j=1;j<e[n-2];++j)
            if(cnt[j]<n2)
        for(i=2;i<n;++i)
            if(f[q][i][j]<inf)
        for(k=2;k<n;++k)
            if(f[q][i][j]+d[i][k]<f[q][k][j|e[k-2]])
                f[q][k][j|e[k-2]]=f[q][i][j]+d[i][k];
    }
    
    ans=inf;
    //枚举先走到的一半为sta
    for(sta=0;sta<e[n-2];++sta)
    if (cnt[sta]==n1)
    {
        //前半段
        x=inf; //x记录前半段的最短距离
        //枚举前一半中最后一个收割的点是i
        for(i=2;i<n;++i)
            if (sta&e[i-2])
            //枚举后一半中第一个收割的点是j
            for(j=2; j<n; ++j)
            if(!(sta&e[j-2]))
                if(f[0][i][sta]+d[i][j]+f[1][j][e[n-2]-1-sta]<x)
                    x=f[0][i][sta]+d[i][j]+f[1][j][e[n-2]-1-sta];
        //后半段
        //枚举前一半中最后一个播种的点是i
        for(i=2;i<n;++i)
            if(sta&e[i-2])
        //枚举后一半中第一个播种的点是j
            for(j=2;j<n;++j)
            if(!(sta&e[j-2]))
                if(x+f[1][i][sta]+d[i][j]+f[0][j][e[n-2]-1-sta]<ans)
                ans=x+f[1][i][sta]+d[i][j]+f[0][j][e[n-2]-1-sta];
    }
    printf("%d\n",ans);
    return 0;
}

信息

难度
9
分类
(无)
标签
(无)
递交数
12
已通过
2
通过率
17%
上传者