BZOJ 1239: [SCOI2008]城堡castle Dp+SA(模拟退火大法好)

谁都会走 提交于 2019-11-26 17:40:26

title

BZOJ 1239
LUOGU 2538
Description

在一个国家里,有n个城市(编号为0 到n-1)。这些城市之间有n条双向道 路相连(编号为0 到n-1),其中编号为i的道路连接了城市i和城市ri(一条道 路可以连接一个城市和它自身),长度为di。n 个城市中有m个拥有自己城堡, 可以抵御敌人侵略。如果没有城堡的城市遭受攻击,则离它最近的城堡将派兵前 往救援。 你的任务是在不超过k个没有城堡的城市中建立城堡,使得所有城市中“离 最近城堡的距离”的最大值尽量小。换句话说,若令dist(c)表示城市c的最近城 堡离它的距离,则你的任务是让max{dist(c)}尽量小。 输入数据保证存在方案使得对于每个城市,至少有一个城堡能够到达。

Input

输入第一行为三个正整数n, m, k。第二行包含n个整数r0,r1,…,rn-1。第三行 包含n 个整数d0,d1,…,dn-1。第四行包含m 个各不相同的0~n-1 之间的整数,分 别为m个城堡所在的城市编号。

Output

输出仅一行,包含一个整数,即max{dist(c)}的最小值。

Sample Input

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

Sample Output

2

HINT

100%的数据满足:2<=n<=50, 1<=di<=106, 0<=m<=n-k

analysis

处理最短路是肯定的,那些本身就有城堡的点就不用管了,直接用他们去更新其他的最短距离。
那些没城堡的点就麻烦了。

怎么处理?二分好啊,外加贪心检验,正如 Tgotp大佬所说,大胆假设,不用证明。然而我的神犇同学 \(Chdy\) 证明出贪心策略是错的,额,再一次献上我的膝盖。

奉上 \(hack\) 掉贪心的数据:
Sample Input

7 0 3
0 0 0 0 1 2 3
1 1 1 1 1 1 1

Sample Output

1

额,这样的话,这道题好像也只有一种出路:随机算法——\(SA\) 模拟退火。

不过忽想起曾经用模拟退火调废一道题的悲惨历史,我...

果断的用了随机排!

极限情况下就把所有没城堡的点都搞了一遍,所以 \(A\)\(A\),看 \(RP\)吧,反正我的同一份代码交上去会出现错误点不一样的情况,不过还好上天照顾,\(A\)了。

code

摆上我的随机排大法

#include<bits/stdc++.h>
using namespace std;
const int maxn=55;

char buf[1<<15],*fs,*ft;
inline char getc() { return (ft==fs&&(ft=(fs=buf)+fread(buf,1,1<<15,stdin),ft==fs))?0:*fs++; }
template<typename T>inline void read(T &x)
{
    x=0;
    T f=1, ch=getchar();
    while (!isdigit(ch) && ch^'-') ch=getchar();
    if (ch=='-') f=-1, ch=getchar();
    while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
    x*=f;
}

template<typename T>inline void write(T x)
{
    if (!x) { putchar('0'); return ; }
    if (x<0) putchar('-'), x=-x;
    T num=0, ch[20];
    while (x) ch[++num]=x%10+48, x/=10;
    while (num) putchar(ch[num--]);
}

int n,m,k;
int Dp[maxn],dp[maxn];
int b[maxn],cnt;
int dist[maxn][maxn];
inline int solve()
{
    memcpy(dp,Dp,sizeof(Dp));
    for (int i=1; i<=k; ++i)
        for (int j=1; j<=n; ++j) dp[j]=min(dp[j],dist[b[i]][j]);
    int ans=0;
    for (int i=1; i<=n; ++i) ans=max(ans,dp[i]);
    return ans;
}

inline void stoch()
{
    random_shuffle(b+1,b+cnt+1);
}

int a[maxn];
bool f[maxn];
int main()
{
    srand(time(NULL));
    read(n);read(m);read(k);
    for (int i=1; i<=n; ++i) read(a[i]),++a[i];

    memset(Dp,0x7f,sizeof(Dp));
    memset(dist,0x3f,sizeof(dist));
    for (int i=1,d; i<=n; ++i) dist[i][i]=0,read(d),dist[i][a[i]]=dist[a[i]][i]=min(dist[i][a[i]],d);//预处理最短路径 
    for (int l=1; l<=n; ++l)
        for (int i=1; i<=n; ++i)
            for (int j=1; j<=n; ++j) dist[i][j]=min(dist[i][j],dist[i][l]+dist[l][j]);//floyed

    for (int i=1,x; i<=m; ++i) read(x),f[++x]=1;//标记有城堡的 
    for (int i=1; i<=n; ++i)
        if (!f[i]) b[++cnt]=i;//没有城堡的一会要处理他们 
        else for (int j=1; j<=n; ++j) Dp[j]=min(Dp[j],dist[i][j]);//有城堡的直接更新就好了,基本不影响答案 

    stoch();
    int T=5,pre,ans=solve();
    while (T--)
    {
        stoch();
        ans=min(ans,solve());
        int G=20;
        while (G--)
        {
            for (int i=k+1; i<=cnt; ++i)
                for (int j=1; j<=k; ++j)
                {
                    swap(b[i],b[j]);
                    pre=ans;
                    ans=solve();
                    if (ans>pre)
                    {
                        ans=pre;
                        swap(b[i],b[j]);
                    }
                }
        }
    }
    write(ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!