• P3959 宝藏


    题意:一个点到其他点的距离等于经过的边数*边权和

       先选一个点,让其与其它点相连,求最小距离和

    输入样例#1: 
    4 5 
    1 2 1 
    1 3 3 
    1 4 1 
    2 3 4 
    3 4 1 
     
    输出样例#1: 
    4
    输入样例#2: 
    4 5 
    1 2 1 
    1 3 3 
    1 4 1 
    2 3 4 
    3 4 2  
    输出样例#2: 
    5

    说明

    当然是爆搜啦~~~~~~~~

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    #define int long long
    int n;
    int m;
    int f[20][20]; 
    int g[20][20]; 
    int cnt;
    int tot;
    int ans=0x7fffffff;
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    inline void in()
    {
        freopen("treasure.in","r",stdin);
        freopen("treasure.out","w",stdout);
    }
    inline void out()
    {
        fclose(stdin);
        fclose(stdout);
    }
    inline void Floyd()
    {
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    g[i][j]=min(g[i][k]+g[k][j],g[i][j]);
        for(int k=1;k<=n;k++)
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    if(i!=j&&j!=k&&k!=i)
                    f[i][j]=min((f[i][k]+f[k][j])*g[i][j],f[i][j]);
    //    for(int i=1;i<=n;i++)
    //        for(int j=1;j<=n;j++)
    //            cout<<i<<" "<<j<<" "<<f[i][j]<<endl;    
    }
    signed main()
    {
        in();
        n=read();
        m=read();
        memset(f,0x3f,sizeof f);
        memset(g,0x3f,sizeof g);
        for(int x,y,z,i=1;i<=m;i++)
        {
            x=read();
            y=read();
            z=read();
            if(x==y)continue;
            f[x][y]=f[y][x]=z;  
            g[x][y]=g[y][x]=1;         
        }
        Floyd();
        for(int i=1;i<=n;i++)
        {
            tot=0;
            for(int j=1;j<=n;j++)
            {
                if(j==i) continue;
                tot+=f[i][j];
            }
            ans=min(ans,tot);
        }
        put(ans);
            out();
        return 0;
    }

    其实,在此之前想到了最小生成树,但是发现不对,(贪心目光短浅QAQ)

    然后,rqjdalao给了一种niubilitiful的算法:模拟退火!

    他可以解决贪心的目光短浅问题

    引入随机化,在某些局面时,不选择局部最优解,而选择次优解、次次优解。。。。

    让算法执行几百次,取min,成功几率很大!

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    using namespace std;
    #define int long long
    int dis[50];
    int num[50];
    int n;
    int m;
    bool vis[50];
    int f[50][50];
    int ans=0x7fffffff;
    int ls;
    struct node
    {
        int id;
        int dis;
        friend bool operator < (const node &a,const node &b)
        {
            return a.dis<b.dis;
        }
    }temp[50];
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    inline void in()
    {
        freopen("treasure.in","r",stdin);
        freopen("treasure.out","w",stdout);
    }
    inline void out()
    {
        fclose(stdin);
        fclose(stdout);
    }
    inline int suiji(int i)
    {
        double x=(double)i/(double)n;
        double y=(double)rand()/(double)RAND_MAX;
        if(y*y>=x) return 2;
        return 1;
    }
    inline int kan_xin_qing(int k)
    {
        int tot=0LL;
        for(int i=1;i<=n;i++)
            if(!vis[i]&&dis[i]*num[i]<0x7fffffff)
                temp[++tot]=(node){i,dis[i]*num[i]};
        sort(temp+1,temp+tot+1);
        if(tot<k)
            return temp[tot].id;
        return temp[k].id;     
    }
    inline int Prim(int head)
    {
        for(int i=0;i<=n;i++)
        {
            vis[i]=0;
            dis[i]=0x3f3f3f3f;
            num[i]=0x3f3f3f3f;
        }
        int tot=0LL;
        dis[head]=num[head]=0LL;
        for(int i=1;i<=n;i++)
        {
            int minn=kan_xin_qing(suiji(i));
            vis[minn]=true;
            tot+=dis[minn]*num[minn];
            // cout<<tot<<" ";
            for(int j=1;j<=n;j++)
            {
                if(!vis[j]&&dis[j]*num[j]>f[minn][j]*(num[minn]+1))
                {
                    dis[j]=f[minn][j];
                    num[j]=num[minn]+1;
                }
            }
        }
        return tot;
    }
    signed main()
    {
        // in();
        srand(time(0));
        n=read();
        m=read();
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                f[i][j]=i==j? 0:0x3f3f3f3f;
        for(int x,y,z,i=1;i<=m;i++)
        {
            x=read();
            y=read();
            z=read();
            if(x==y)continue;
            f[x][y]=f[y][x]=min(f[x][y],z);       
        }
        for(int i=1;i<=n*800;i++)
        {
            ls=0x7fffffff;
            for(int j=1;j<=n;j++)
                ls=min(ls,Prim(j));
            // cout<<ls<<" ";
            ans=min(ans,ls);
        }
        put(ans);
        out();
        return 0;
    }

     当然,看到n那么小,自然有了状压DP的做法

    二进制每一位代表当前有没有打通到i的路

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<ctime>
    #include<queue>
    using namespace std;
    #define int long long
    int dis[50];
    int n;
    int m;
    int f[50][50];
    int ans=0x7fffffff;
    int ls;
    int dp[655360];
    inline int read()
    {
        int x=0,f=1;
        char ch=getchar();
        while(!isdigit(ch))
        {
            if(ch=='-')
                f=-f;
            ch=getchar();
        }
        while(isdigit(ch))
        {
            x=(x<<1)+(x<<3)+(ch^48);
            ch=getchar();
        }
        return x*f;
    }
    inline void put(int x)
    {
        if(x<0)
        {
            x=-x;
            putchar('-');
        }
        if(x>9)
            put(x/10);
        putchar(x%10+'0');
    }
    inline void in()
    {
        freopen("treasure.in","r",stdin);
        freopen("treasure.out","w",stdout);
    }
    inline void out()
    {
        fclose(stdin);
        fclose(stdout);
    }
    inline void dfs(int zt)
    {
        for(int i=1;i<=n;i++)    //遍历所有点
        {
            if((1<<(i-1))&zt)      //如果当前状下i已被遍历
            {
                for(int j=1;j<=n;j++)    //遍历与i相邻的点
                {
                    if(!((1<<(j-1))&zt)&&f[i][j]<=0x7fffffff)   //当前为遍历j并且i与j相连
                    {
                        if(dp[(1<<(j-1))|zt]>dp[zt]+dis[i]*f[i][j])    //可更新
                        {
                            int ls=dis[j];
                            dis[j]=dis[i]+1;                          //ls用于回溯
                            dp[(1<<(j-1))|zt]=dp[zt]+dis[i]*f[i][j];   //更新 
                            dfs((1<<(j-1))|zt);        //让j进入状态0->1
                            dis[j]=ls;   //回溯
                        }
                    }
                }
            }
        }
    }
    signed main()
    {
        // in();
        n=read();
        m=read();
        memset(f,0x5f,sizeof f);
        for(int x,y,z,i=1;i<=m;i++)
        {
            x=read();
            y=read();
            z=read();
            if(x==y)continue;
            f[x][y]=f[y][x]=min(f[x][y],z);       
        }
        for(int i=1;i<=n;i++)   //枚举每个点为起点
        {
            memset(dis,0x7f,sizeof dis);   //每次赋初值
            memset(dp,0x7f,sizeof dp);
            dp[1<<(i-1)]=0;     //起点(起点是1,其余为0)
            dis[i]=1;        
            dfs(1<<(i-1));      //从起点开始搜
            ans=min(ans,dp[(1<<n)-1]);   //最终状态,全是1
        }
        put(ans);
        out();
        return 0;
    }
  • 相关阅读:
    容器技术之Dockerfile(一)
    容器技术之Docker常用命令说明
    容器技术之Docker数据卷
    Cypress与TestCafe WebUI端到端测试框架简介
    API测试之Postman使用全指南(原来使用 Postman测试API如此简单)
    【PyMuPDF和pdf2image】Python将PDF转成图片
    2020 | 可替代Selenium的测试框架Top15
    AWS EC2+Docker+JMeter构建分布式负载测试基础架构
    Appium移动端自动化测试--元素操作与触摸动作
    Appium移动端自动化测试--使用IDE编辑并强化脚本
  • 原文地址:https://www.cnblogs.com/olinr/p/9570584.html
Copyright © 2020-2023  润新知