图型dp csu1700 Black Company

传送门:点击打开链接

题意:有n个人,每个人都有学历。然后m个关系,每个关系有u和v,表示u和v是朋友

现在要求,如果是u和v是朋友,那么学历高的人的工资就必须比学历低的人的工资高

如果u和v都是x的朋友,那么对于u和v,那么学历高的人的工资就必须比学历低的人的工资高

说白了就是,通过1条边或者2条边相连的点之间,学历高的工资要更高

问这样去安排工资,那么总工资最低是多少。

思路:因为最多关系中只会相差2条边,所以我们当然会去考虑dp


对于每个节点u,我们去维护Max1和Max2。表示u节点的周围学历比u的学历要低的点中,工资最大值和次大值。

还要顺便维护一个nam,表示工资最大的那个的学历是多少。

首先,我们读入人的学历,按学历从小到大排序,然后从小到大考虑人的工资。

对于点u,假如v是u的相邻节点,那么u的工资会等于max(Max1[v],Max1[u])+1

但是这样写还是不够的,因为如果它周围的最大工资的那个人,实际上学历和它是一样的,那么没必要+1

所以这里我们才维护了次大值,当取最大值的那个nam等于u的工资时,那么我们就只需要用次大值工资去更新了,这样答案会更优。

在计算完u的答案之后,我们还要遍历一遍v,维护下v的Max1和Max2

最后把所有节点的答案累加输出即可

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<bitset>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;

const int MX = 4e5 + 5;
const int INF = 0x3f3f3f3f;
const int mod = 1e9 + 7;

int Head[MX], erear;
struct Edge {
    int v, nxt;
} E[MX * 2];
void edge_init() {
    erear = 0;
    memset(Head, -1, sizeof(Head));
}
void edge_add(int u, int v) {
    E[erear].v = v;
    E[erear].nxt = Head[u];
    Head[u] = erear++;
}

int ans[MX];
int Max1[MX], Max2[MX], nam[MX];
struct Data {
    int id, x;
    bool operator<(const Data &P) const {
        return x < P.x;
    }
} A[MX];

int main() {
    int n, m; //FIN;
    while(~scanf("%d", &n)) {
        edge_init();
        memset(Max1, 0, sizeof(Max1));
        memset(Max2, 0, sizeof(Max2));
        memset(nam, 0, sizeof(nam));

        for(int i = 1; i <= n; i++) {
            A[i].id = i;
            scanf("%d", &A[i].x);
        }
        sort(A + 1, A + 1 + n);

        scanf("%d", &m);
        for(int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d%d", &u, &v);
            edge_add(u, v);
            edge_add(v, u);
        }

        for(int i = 1; i <= n; i++) {
            Data tp = A[i];
            if(tp.x == nam[tp.id]) ans[tp.id] = Max2[tp.id] + 1;
            else ans[tp.id] = Max1[tp.id] + 1;
            for(int i = Head[tp.id]; ~i; i = E[i].nxt) {
                int v = E[i].v;
                if(tp.x == nam[v]) ans[tp.id] = max(ans[tp.id], Max2[v] + 1);
                else ans[tp.id] = max(ans[tp.id], Max1[v] + 1);
            }

            for(int i = Head[tp.id]; ~i; i = E[i].nxt) {
                int v = E[i].v;
                if(ans[tp.id] > Max1[v]) {
                    Max2[v] = Max1[v];
                    Max1[v] = ans[tp.id];
                    nam[v] = tp.x;
                } else if(ans[tp.id] > Max2[v]) {
                    Max2[v] = ans[tp.id];
                }
            }
        }

        LL res = 0;
        for(int i = 1; i <= n; i++) {
            res += ans[i];
        }
        printf("%lld\n", res);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值