【最优高铁环】 分数规划,dfs判负环

1 篇文章 0 订阅
1 篇文章 0 订阅

牛客上此题链接.

这里所用的分数规划的公式是len1+len2+len3 = ans * 3
即 0 = ans - len1 + ans - len2 + ans - len3
用负环来判断这个公式中ans是否能够继续变大
原因是如果用ans - len 作为每一个边的权重,那么负环就是相当于得到 0 > ans - len1 + ans - len2 + ans - len3ans可以变大,所以就这样达到目的。
注意的是不能够把len - ans作为边的权重,即不能够把边权取相反数,因为上面判负环的情形是只要有负环存在,那么就能够获得更大的ans。 但是如果去了反之后, 就是只要有负环存在,ans就能够继续变小,通过这个判定无法得知当前ans是否能够变大,反而只是知道了ans是否能够变小,如果硬要用边权取反的形式来判断应该是需要保证有至少一个正环的情况下可以让ans继续变大。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define min(i, j) (i < j ? i : j)
#define max(i, j) (i > j ? i : j)
const int maxn = 5e5 + 60;
const int maxm = 5e5 + 60;

vector<int> G[maxn];
vector<int> l[maxn];

map<string, int> ma;

int orival[maxn];
char saveori[maxn];
char temp[maxn];

double dis[maxn];
int inqcnt[maxn];
int inq[maxn];
int idcnt = 0;
int vis[maxn];
int dfs(int s, double ans)
{
    vis[s] = 1;
    for (int i = 0; i < G[s].size(); i++)
    {
        int d = G[s][i];
        int len = l[s][i];
        //double reallen = (len * 1.0 / ans - 1);
        double reallen = len - ans;
        if (dis[d] > dis[s] + reallen)
        {
            dis[d] = dis[s] + reallen;
            if (vis[d] || dfs(d, ans))
                return 1;
        }
    }
    vis[s] = 0;
    return 0;
}

bool func(int s, double ans)
{
    for (int i = 1; i <= idcnt; i++)
    {
        dis[i] = 33333333333.3;
        vis[i] = 0;
    }
    for (int i = 1; i <= idcnt; i++)
    {
        if (dfs(i, ans)){
            // return 1;
        }
        else
        {
            return 1;
        }
    }
    return 0;
}

void solve()
{
    int n;
    cin >> n;
    int val = 0;
    for (int i = 1; i <= n; i++)
    {
        val = 0;
        scanf("%s", saveori + 1);
        int len = strlen(saveori + 1);
        int arr = 1;
        string s = "", t = "";
        while (arr <= len)
        {
            int temparr = 0;
            if (saveori[arr] == ' ')
            {
                arr++;
                continue;
            }
            while (saveori[arr] != '-' && saveori[arr] != '\n' && arr <= len)
            {
                temp[temparr++] = saveori[arr++];
            }
            arr++;
            if (temparr)
            {
                if (temp[0] == 'S')
                {
                    val += 1000;
                }
                else if (temp[0] == 'G')
                {
                    val += 500;
                }
                else if (temp[0] == 'D')
                {
                    val += 300;
                }
                else if (temp[0] == 'T')
                {
                    val += 200;
                }
                else if (temp[0] == 'K')
                {
                    val += 150;
                }
            }
            temp[temparr] = '\0';
            if (s.size() == 0)
            {
                s = string(temp);
            }
            t = string(temp);
        }
        if (!ma.count(t))
        {
            ma[t] = ++idcnt;
        }
        if (!ma.count(s))
        {
            ma[s] = ++idcnt;
        }
        G[ma[s]].push_back(ma[t]);
        l[ma[s]].push_back(val);
    }
    double r = 0x7fffffff;
    double l = 0.00000001;
    double ans = -1.0;
    while (r - l >= 0.0001)
    {
        double mid = (r + l) / 2.0;
        int res = func(1, mid);
        if (res == 0)
        {
            ans = mid;
            l = mid;
        }
        else
        {
            r = mid;
        }
    }
    // ans = (l + r) / 2;
    printf("%.0f\n", ans);
}

int main()
{
    // cout << (1500.0 / 1285 - 1.0) + (1500.0 / 1285 - 1.0) + (850.0 / 1285 - 1.0) << endl;
    freopen("in.txt", "r", stdin);
    int T;
    solve();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值