GDOI2016模拟8.15安全监控

题目
选举越来越近了,所以总统Amabo Kcarab准备在美国计划一次旅行,并在WDC和LA进行演讲。特务为了能够保护总统的安全,需要时刻监控所有总统会经过的城市(包括WDC和LA)。

当然,为了使预算不会太大,总统不会用到AF1,而会用汽车作为交通工具。并且,特务计划在总统从WDC到LA和回到WDC的旅途中安排尽可能少的需要监控的城市数目。

对于这个问题,我们假设美国有N个城市,标号为1到N,和M条单向的连接两个不同城市的州际公路。WDC的编号为1,LA的编号为2。

写一个程序计算出最少需要被监控的城市,使得有一条路只经过被监控的城市,可以从WDC到LA,最后回到WDC。

注意:测试数据保证有解。

N,M(2<=N<=100,2<=M<=200),分别表示城市的数目和州际公路的数目。

这题注意到N比较小、M也比较小

我就想着先用floyd来跑出每个点对间的最短路,然后若i能到j,j能到i,则记录下点对(i,j),令leni,j为其正反最短路之和,那么leni,j为i到j再到i的答案上界。
但还不够,我们用堆来从小到大做,若存在leni,j、lenj,k、leni,k且leni,j+lenj,k-1<leni,k则更新leni,k,相当于两个环公用了一个点,只算一次。
这样可以处理出相对较优的答案上界,这时候可以暴力(注意,每个点最多经2次),用最优性剪枝,就可以过了

贴代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
#define N 101
int n,m,top,ans,s,t;
int dis[N][N],f[N*N][2],bz[N][N],g[N],a[N*4][2];
int bz1[N];
void ins1(int x,int y){
    static int sum=0;
    a[++sum][0]=y,a[sum][1]=g[x],g[x]=sum;
}
void init(){
    static int x,y;
    scanf("%d %d",&n,&m);
    for (int i=1;i<=m;i++){
        scanf("%d %d",&x,&y);
        dis[x][y]=1;
        ins1(x,y);
    }
}
void Swap(int x,int y){
    swap(f[x][0],f[y][0]),swap(f[x][1],f[y][1]);
    bz[f[x][0]][f[x][1]]=x,bz[f[y][0]][f[y][1]]=y;
}
bool jian(int x,int y){
    return dis[f[x][0]][f[x][1]]<dis[f[y][0]][f[y][1]];
}
void up(int x){
    static int y;
    y=x/2;
    while (y&&jian(x,y))
        Swap(x,y),x=y,y/=2;
}
void down(int x){
    static int y;
    y=x+x;
    while ((y<=top&&jian(y,x))||(y<top&&jian(y+1,x))){
        if (y<top&&jian(y+1,y))y++;
        Swap(x,y),x=y,y+=y;
    }
}
void ins(int x,int y){
    f[++top][0]=x,f[top][1]=y,bz[x][y]=top;
    up(top);
}
void del(){
    Swap(top--,1);
    down(1);
}
void pre(){
    for (int i=1;i<=n;i++)
        for (int j=1;j<=n;j++)
            if (i!=j&&dis[j][i])
                for (int k=1;k<=n;k++)
                    if (i!=k&&j!=k&&dis[i][k])
                        if (!dis[j][k]||dis[j][k]>dis[j][i]+dis[i][k])
                            dis[j][k]=dis[j][i]+dis[i][k];
    for (int i=1;i<=n;i++)
        for (int j=i+1;j<=n;j++)
            if (dis[i][j]&&dis[j][i])
            dis[i][j]+=dis[j][i],ins(i,j);
}
void did(int x,int y,int xx,int yy,int xxx,int yyy){
    if (!dis[x][y]||!dis[y][x]||!dis[xxx][yyy]||!dis[yyy][xxx])return;
    if (x>y)swap(x,y);
    if (xx>yy)swap(xx,yy);
    if (xxx>yyy)swap(xxx,yyy);
    if (dis[xxx][yyy]>dis[x][y]+dis[xx][yy]-1){
        dis[xxx][yyy]=dis[x][y]+dis[xx][yy]-1;
        up(bz[xxx][yyy]);
    }
}
void dfs(int x,int y,bool p){
    if (x==t){
        if (p)return;
        p=1;
    }
    if (ans<=y)
        return;
    if (x==s&&bz1[x]!=1){
        if (p)ans=y;
        return;
    }
    for (int i=g[x];i;i=a[i][1])
        if (bz1[a[i][0]]!=2){
            bz1[a[i][0]]++;
            if (bz1[a[i][0]]==1)
            dfs(a[i][0],y+1,p);
            else
            dfs(a[i][0],y,p);
            bz1[a[i][0]]--;
        }
}
void work(){
    static int x,y;
    while (top){
        x=f[1][0],y=f[1][1],del();
        for (int i=1;i<=n;i++)
            if (i!=x&&i!=y){
                did(i,x,x,y,i,y);
                did(i,y,x,y,i,x);
            }
    }
    ans=dis[1][2];
    s=1,t=2;
    bz1[1]=1;
    dfs(1,1,0);
}
void write(){
    printf("%d",ans);
}
int main(){
    init();
    pre();
    work();
    write();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值