负环与01分数规划——观光奶牛

01分数规划,简单的来说,就是有一些二元组(si,pi),从中选取一些二元组,使得∑si / ∑pi最大(最小)。

这种题一类通用的解法就是,我们假设x = ∑si / ∑pi的最大(小)值,那么就有x * ∑pi = ∑si ,即∑si  - x * ∑pi= 0。也就是说,当某一个值x满足上述式子的时候,它就是要求的值。我们可以想到枚举……不过再想想,这个可以二分答案。

所以我们直接二分答案,当上述式子>0,说明答案小了,<0则说明答案大了,这样计算即可。

(以上来自网络)

传送门:361. 观光奶牛 - AcWing题库

思路:设环上各点的权值为f[i],环上各边的权值为t[k],由题目可的Σf[i]/Σt[k]==mid,使得mid的值最大,换一下就是找到使得Σf[i]-mid*Σt[k]>0成立的最大mid的值。

找mid的过程就是二分的过程。

f[i]-mid*t[k]含义:点i的权值减去mid乘点i的出边的边权。

对于上面的Σf[i]-mid*Σt[k],建立一个新图使得边权为f[i]-mid*t[k],在新图里面进行找最长路的操作,当mid取值太小时,Σf[i]-mid*Σt[k]会小于0,此时会存在负环,但因为是求最长路,并不会有某一个点的入队次数超过n,最后会返回false,存在正环的时候就会返回true。true说明mid取小了,使二分边界 l =mid ,返回false就使r=mid,最后使得abs(r-l)小于1e-4,可以确保保留两位小数的数值准确。

代码:
 

#include
#include
#include
#include
using namespace std;
typedef long long ll;
const int N=1010,M=5010;
int n,m;
int wf[N];
int h[N],wt[M],idx,ne[M],e[M];
double dist[N];
bool st [N];
int q[N],cnt[N];
void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],wt[idx]=c,h[a]=idx++;
}
bool check(double mid)
{
    memset(st,0,sizeof st);
    memset(cnt,0,sizeof cnt);
    memset(dist,0,sizeof dist);
    int hh=0,tt=0;
    for(int i=1;i<=n;i++)
    {
        q[tt++]=i;
        st[i]=true;
    }
    while(hh!=tt)
    {
        int t=q[hh++];
        if(hh==N) hh=0;
        st[t]=false;
        for(int i=h[t];~i;i=ne[i])
        {
            int j=e[i];
            if(dist[j]=n) return true;
                 if(!st[j])
                {
                    q[tt++]=j;
                    if(tt==N) tt=0;
                    st[j]=true;
                }
            }
        }
    }
    return false;
}



int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    for(int i=1;i<=n;i++)
        scanf("%d",&wf[i]);
    while(m--)
    {
        int a,b,c;
        scanf("%d%d%d",&a,&b,&c);
        add(a,b,c);
    }
    double l=0,r=1e6;
    while(r-l>1e-4)
    {
        double mid=(l+r)/2;
        if(check(mid)) l=mid;
        else r=mid;
    }
    printf("%.2lf\n",r);

    return 0;
}

你可能感兴趣的:(#,spfa扩展——负环与差分约束,算法,蓝桥杯,c++)