POJ3621:Sightseeing Cows——题解

http://poj.org/problem?id=3621

全文翻译参自洛谷:https://www.luogu.org/problemnew/show/P2868

题目大意:一个有向图,每个点都有一个价值,每条路通过需要一定时间,求出一个回路使得价值和/时间和最大。(重复经过一个点不会额外增加价值)

按照01分数规划的套路,我们显然可以将路的边权更改为时间*枚举的答案-目的地价值,然后找一个环。

如果这个环是一个负环,那么显然答案还可以变得更大,反之则需要变小。

所以我们需要用spfa判断图中的负环。但是bfs-spfa显然太慢,所以我们需要更高效的算法——dfs-spfa,需要优化的地方。

1.由于我们的目的不是为了求最短路,所以大可以不必将dis全部清成inf(其实连清空都不需要,可以感性理解)。

2.dfs-spfa的好处在于一直搜,直到搜到我们找到的点已经被找过为止。

3.剩下的就是spfa的基本操作了——当dis被更新的时候才能走这个点。

(double和int傻傻搞不清楚……)

#include<cstdio>
#include<cmath>
#include<cstring>
#include<queue>
#include<cctype>
#include<algorithm>
using namespace std;
typedef double dl;
const dl eps=1e-7;
const int INF=2147483647;
const int M=5001;
const int N=1001;
inline int read(){
    int X=0,w=0;char ch=0;
    while(!isdigit(ch)){w|=ch=='-';ch=getchar();}
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    return w?-X:X;
}
struct node{
    int to,nxt,l;
}e[M];
int head[N],f[N],cnt,n,m;
bool vis[N];
dl dis[N],w[M];
inline void add(int u,int v,int l){
    cnt++;
    e[cnt].to=v;
    e[cnt].l=l;
    e[cnt].nxt=head[u];
    head[u]=cnt;
    return;
}
bool spfa(int u){
    vis[u]=1;
    for(int i=head[u];i;i=e[i].nxt){
    int v=e[i].to;
    if(dis[v]>dis[u]+w[i]){
        dis[v]=dis[u]+w[i];
        if(vis[v]||spfa(v)){
        vis[v]=0;
        return 1;
        }
    }
    }
    vis[u]=0;
    return 0;
}
bool pan(){
    for(int i=1;i<=n;i++){
    if(spfa(i))return 1;
    }
    return 0;
}
int main(){
    n=read();m=read();
    for(int i=1;i<=n;i++)f[i]=read();
    for(int i=1;i<=m;i++){
    int u=read(),v=read(),l=read();
    add(u,v,l);
    }
    dl l=0,r=100000;
    while(r-l>eps){
    dl mid=(l+r)/2;
    for(int i=1;i<=cnt;i++){
        int v=e[i].to;
        w[i]=(dl)mid*e[i].l-f[v];
    }
    if(pan())l=mid;
    else r=mid;
    }
    printf("%.2f\n",l);
    return 0;
}

 

posted @ 2018-01-11 14:21  luyouqi233  阅读(293)  评论(0编辑  收藏  举报