【luoguP1453】城市环路



题目背景

一座城市,往往会被人们划分为几个区域,例如住宅区、商业区、工业区等等。B市就被分为了以下的两个区域——城市中心和城市郊区。在着这两个区域的中间是一条围绕B市的环路,环路之内便是B市中心。

题目描述

整个城市可以看做一个N个点,N条边的单圈图(保证图连通),唯一的环便是绕城的环路。保证环上任意两点有且只有2条路径互通。图中的其它部分皆隶属城市郊区。

现在,有一位名叫Jim的同学想在B市开店,但是任意一条边的2个点不能同时开店,每个点都有一定的人流量Pi,在该点开店的利润就等于该店的人流量Pi×K(K≤10000),K的值将给出。

Jim想尽量多的赚取利润,请问他应该在哪些地方开店?

输入输出格式

输入格式:

第一行一个整数N 代表城市中点的个数。城市中的N个点由0~N-1编号。

第二行N个正整数,表示每个点的人流量Pi(Pi≤10000)。

下面N行,每行2个整数A,B,表示A,B建有一条双向路。

最后一行一个实数K。

输出格式:

一个实数M,(保留1位小数),代表开店的最大利润。

输入输出样例

输入样例#1:
4
1 2 1 5
0 1
0 2
1 2
1 3
2
输出样例#1:
12.0

说明

【数据范围】

对于20%的数据 N≤100.

对于50%的数据 N≤2000.

对于100%的数据 N≤100000(环上的点不超过2000个).



这题是相当的坑。。。

坑点相当恶心


先说一下题解:

这题其实是一道非常显然的DP

然后我们对于其他不在环上的点,可以发现可以树形DP预处理出来


然后然后就只剩下了一个环,

直接跑环形DP就好了


然后对于起点,选和不选要跑两遍


接下来说一下坑点:

如果有想到了O(环上的点^2)的算法,写完代码交了以后就会和我一样惊奇地发现


这题数据环上的点并没有保证不超过2000个!!!!!!!


我搞了一个晚上才发现这一点。。。。。。。


PS.这题蒟蒻发现和以前写的环形DP不一样,只需要一个起点跑一次就好了,仔细想想以后发现原来是以前的写过的环形DP出发点不一样会产生不同结果


代码:

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<vector>
#include<queue>
#include<stack>
using namespace std;

typedef long long LL;
typedef double DL;

const int maxn = 200100;

vector<int> e[maxn];
queue<int> Q;
DL p[maxn],a[maxn][2],f[maxn][2],k;
bool vis[maxn],exist[maxn],flag = 0;
int pre[maxn],stk[maxn],top,point[maxn],tot;
int n;

inline LL getint()
{
    LL ret = 0,f = 1;
    char c = getchar();
    while (c < '0' || c > '9')
    {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        ret = ret * 10 + c - '0',c = getchar();
    return ret * f;
}

inline void find(int u,int fa)
{
    vis[u] = 1;
    stk[++top] = u;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (flag) return;
        if (v == fa) continue;
        if (vis[v])
        {
            while (stk[top] != v)
            {
                exist[stk[top]] = 1;
                point[++tot] = stk[top--];
            }
            exist[stk[top]] = 1;
            point[++tot] = stk[top--];
            flag = 1;
            return;
        }
        find(v,u);
    }
    stk[top] = 0;
    top--;
    return;
}

inline void init(int u,int fa)
{
    a[u][1] = p[u] * k;
    for (int i = 0; i < e[u].size(); i++)
    {
        int v = e[u][i];
        if (v == fa) continue;
        if (exist[v]) continue;
        init(v,u);
        a[u][0] += max(a[v][1],a[v][0]);
        a[u][1] += a[v][0];
    }
}

int main()
{
    n = getint();
    for (int i = 1; i <= n; i++) p[i] = getint();
    for (int i = 1; i <= n; i++)
    {
        int u = getint() + 1,v = getint() + 1;
//        int u = getint(),v = getint();
        e[u].push_back(v);
        e[v].push_back(u);
    }
    scanf("%lf",&k);
    memset(vis,0,sizeof(vis));
    find(1,0);
    memset(vis,0,sizeof(vis));
    for (int i = 1; i <= n; i++)
        init(i,0);
    DL ans = 0;
    n = tot;
    f[1][0] = a[point[1]][0];
    f[1][1] = 0;
    for (int j = 2; j <= n; j++)
    {
        f[j][1] = f[j - 1][0] + a[point[j]][1];
        f[j][0] = max(f[j - 1][1],f[j - 1][0]) + a[point[j]][0];
    }
    ans = max(ans,max(f[n][1],f[n][0]));
    f[1][0] = 0;
    f[1][1] = a[point[1]][1];
    for (int j = 2; j <= n; j++)
    {
        f[j][1] = f[j - 1][0] + a[point[j]][1];
        f[j][0] = max(f[j - 1][1],f[j - 1][0]) + a[point[j]][0];
    }
    ans = max(ans,f[n][0]);
    printf("%.1lf",ans);
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值