Luogu P3959 宝藏

这道题正解是状压DP,不过我不会所以写一下随机化算法来骗骗分。

听说当时考场上就有很多写prim然后挂掉的神仙,其实这道题是可以prim过的

prim是一种基于贪心的算法,在本题中由于盲目的选择当前最优解可能会使得后面的决策不优,于是我们请出基于随机化的prim我口胡的

每一次选择边的时候,有概率的跳过一些对于当前来说最优的边,这样为后面可能跑出更优的解做出铺垫。

这样一次可能得到的解还不如直接贪心得到的解优,但是我复杂度这么小,跑个几千次又怎样?

然后选好随机数种子,满怀信仰地祈祷就可以AC了

虽然这不是严格意义上的模拟退火,但是随机化的思想还是比较神仙的

CODE

#include<cstdio>
#include<cctype>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<ctime>
using namespace std;
const int N=15,M=1005;
int edge[N][N],n,m,x,y,z,dep[N],ans,INF;
bool vis[N];
struct data
{
    int fr,to;
    bool operator <(const data a) const { return dep[a.fr]*edge[a.fr][a.to]<dep[fr]*edge[fr][to]; }
}stack[M];
priority_queue <data> small;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(int &x)
{
    x=0; char ch; while (!isdigit(ch=tc()));
    while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc()));
}
inline int Simulate_Anneal(int st)
{
    memset(dep,0,sizeof(dep)); memset(vis,0,sizeof(vis)); 
    register int i; int tot=0,top=0; memset(stack,0,sizeof(stack));
    while (!small.empty()) small.pop(); dep[st]=vis[st]=1;
    for (i=1;i<=n;++i) if (edge[st][i]<INF) small.push((data){st,i});
    for (i=1;i<n;++i)
    {
        data e=small.top(); small.pop();
        while (!small.empty()&&(vis[e.to]||!(rand()%n)))
        {
            if (!vis[e.to]) stack[++top]=e;
            e=small.top(); small.pop();
        }
        vis[e.to]=1; dep[e.to]=dep[e.fr]+1; tot+=dep[e.fr]*edge[e.fr][e.to];
        while (top) small.push(stack[top--]);
        for (register int j=1;j<=n;++j)
        if (!vis[j]&&edge[e.to][j]<INF) small.push((data){e.to,j});
    }
    return tot;
}
inline void miner(int &x,int y)
{
    x=y<x?y:x;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register int i,t=200; read(n); read(m); srand(20030909);
    memset(edge,63,sizeof(edge)); ans=INF=edge[0][0];
    for (i=1;i<=m;++i)
    {
        read(x); read(y); read(z);
        miner(edge[x][y],z); miner(edge[y][x],z);
    }
    while (t--)
    for (i=1;i<=n;++i)
    miner(ans,Simulate_Anneal(i));
    return printf("%d",ans),0;
}
posted @ 2018-08-21 19:48  空気力学の詩  阅读(123)  评论(0编辑  收藏  举报