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;
}

猜你喜欢

转载自www.cnblogs.com/olinr/p/9570584.html
今日推荐