【Dijkstra+贪心】BZOJ1922(Sdoi2010)[大陆争霸]题解

题目概述

n 个点 m 条有向边,一个点要被摧毁需要先摧毁其他的一些点,求从 1 开始,摧毁 n 的最短时间。

解题报告

刚开始以为是拓扑,之后发现显然不是,因为一个点没被摧毁之前是不能访问的。

所以SPFA也不能用了,只剩下Dijkstra。记录 dis[0][i] 表示到 i 点的最短时间, dis[1][i] 表示 i 可以被摧毁的最短时间。那么一个点的摧毁时间就是 dis[i]=max{dis[0][i],dis[1][i]} ,每次用最小的 dis[i] 来修正就行了。

这么贪心应该是对的吧……

示例程序

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fr first
#define sc second
#define mp make_pair
using namespace std;
typedef long long LL;typedef pair<int,LL> data;
const int maxn=3000,maxm=1e6;
​
int n,m,f[maxn+5];LL dis[2][maxn+5];bool vis[maxn+5];
int E,lnk[2][maxn+5],son[maxm+5],nxt[maxm+5],w[maxm+5];
int si;data Heap[maxn+maxm+5];
​
inline void Add(int x,int y,int z) {son[++E]=y;w[E]=z;nxt[E]=lnk[0][x];lnk[0][x]=E;}
inline void Add(int x,int y) {son[++E]=y;nxt[E]=lnk[1][x];lnk[1][x]=E;}
inline bool cmp(const data &a,const data &b) {return a.sc>b.sc;}
#define dis(x) max(dis[0][x],dis[1][x])
#define Push(x) Heap[++si]=(x),push_heap(Heap+1,Heap+1+si,cmp)
#define Pop() pop_heap(Heap+1,Heap+1+si--,cmp)
void Dij()
{
    memset(dis[0],63,sizeof(dis[0]));dis[0][1]=dis[1][1]=0;Push(mp(1,dis(1)));
    while (si)
    {
        int k=Heap[1].fr;while (vis[k]) Pop(),k=Heap[1].fr;
        if (k==n) break;vis[k]=true;LL MIN=Heap[1].sc;Pop();
        for (int j=lnk[0][k];j;j=nxt[j]) if (!vis[son[j]])
        {
            int u=son[j];dis[0][u]=min(dis[0][u],MIN+w[j]);
            if (!f[u]) Push(mp(u,dis(u)));
        }
        for (int j=lnk[1][k];j;j=nxt[j]) if (!vis[son[j]])
        {
            int u=son[j];dis[1][u]=max(dis[1][u],MIN);f[u]--;
            if (!f[u]) Push(mp(u,dis(u)));
        }
    }
}
int main()
{
    freopen("program.in","r",stdin);
    freopen("program.out","w",stdout);
    scanf("%d%d",&n,&m);
    for (int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&x,&y,&z),Add(x,y,z);
    for (int i=1,x;i<=n;i++)
        {scanf("%d",&f[i]);for (int t=1;t<=f[i];t++) scanf("%d",&x),Add(x,i);}
    return Dij(),printf("%lld\n",dis(n)),0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值