luogu3872 [TJOI2010]电影迷

(http://www.elijahqi.win/2018/01/02/luogu3872-tjoi2010%E7%94%B5%E5%BD%B1%E8%BF%B7/)
题目描述

小A是一个电影迷,他收集了上百部的电影,打算从中挑出若干部在假期看完。他根据自己的口味和网上的介绍,对每部电影X都打了一个分数vX,表示自己喜欢的程度。这个分数的范围在-1000至1000之间,越大表示越喜欢。小A每看一部电影X,他的体验值就会加上vX。

另外,因为某些电影是组成一个系列的,比如著名的《终结者》系列、《黑客帝国》系列等等,如果小A只看了前一部而没有看后一部的话,他就会觉得不是很爽。准确来讲,对于任意两部不同的电影X,Y,他们可能存在一个依赖值dXY,表示如果小A看了X但是没看Y,他的体验值就会减少dXY。(注意与观看的顺序无关,只要两部都看过,就不会减少体验值)

现在他要选出若干电影来看,使得得到的总的体验值最大。如果他无法得到正的体验值,就输出0。
输入输出格式

输入格式:

输入的第一行是两个整数:电影总数N和依赖关系数目M。第二行包含用空格隔开的N个数,表示对每部电影的打分。接下来M行,每行包含三个整数X, Y, dXY,表示一个依赖关系。每个有序对(X,Y)最多出现一次。(1 ≤ X,Y ≤ N)

输出格式:

输出一个整数,表示小A能得到的最大体验值。
输入输出样例

输入样例#1: 复制

2 2
100 -50
1 2 49
2 1 10

输出样例#1: 复制

51

说明

如果小A只看电影1,体验值为100-49 = 51。如果只看电影2,体验值为-50-10 = -60。如果两部都看,体验值为100+(-50) = 50。所以应该只看电影1。

数据规模与约定

对于20%的数据,1 ≤ N ≤ 15

对于100%的数据,1 ≤ N ≤ 100, -1000 ≤ vX ≤ 1000, 0 < dXY ≤ 1000

每个测试点时限1秒

这题可以看作是最大权闭合子图的一个扩展问题 其实和那个bzoj ceoi2008 order挺像的 想了一天也没想到方法去膜了下题解 发现其实有一种情况我是可以不去考虑的 比如我这个电影的值是负数 如果选了他 没选别的 也会有一个代价 那其实这条边我是始终也不需要考虑的

我首先从源点向每个权值为正的电影连边 然后如果这个电影的权值为负数 那么我就从这个电影向汇点连边 然后如果每个电影之间存在关系 那么直接连边即可 这样的话就被转化成一个最小割问题 这个问题分为三个部分 首先是我不看这个电影带来的损失(注意如果存在关联 且没看是在现有的基础上倒扣 所以这个建图成立 其次如果存在关联 我不去看后面的电影也有可能带来损失 最后如果我必须得看这个电影 那么因为权值是负数 所以也会带来损失 跑一下最大流 用正权的总和-最大流即可

唉还是太菜啊 怎么办呢qwq

另外跪膜Icefox大佬 提供的另外一种做法 是什么呢 就是因为存在负的 所以我可以 用1000-一下每个权值 这样求出来的最小割 然后再用点数*1000- 最小割就是我想要的最大的答案了 因为这题其实我读题的时候还是有误差 觉得自己太菜了 仍然 要努力啊 做法就是 我源向每个点连 1000的 边 然后每个点向 汇连 1000-权值的边 然后 如果有限制 那么就在两个点之间直接嗯连限制的那个权值的边 然后就像我刚刚说的 用点数*1000- 最小割 为什么这么做 其实道理差不多 因为 相当于我这个点可以不选 那么我就没有收益了 这1000的边就被我割去了 如果互相之间有关联的话 如果那个权值我没有选的话 那么是不是我相当于是这1000就没了 那我还要限制一下如果选了那个点 没选这个点之间的边 要么割掉 要么不选那个点 一开始我认为这1000的权值太小了 如果我选了这个点 不选其他点的代价太大 那么会不够好 显然是不对的 因为如果我最终收益肯定<0那么我干脆哪个都不选为0即可


#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define inf 0x3f3f3f3f
#define N 110
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if(T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0'){x=x*10+ch-'0';ch=gc();}
    return x*f;
}
struct node{
    int y,z,next;
}data[N*N*2];
int num=1,h[N],level[N],n,m,T;
inline void insert1(int x,int y,int z){
    data[++num].y=y;data[num].z=z;data[num].next=h[x];h[x]=num;
    data[++num].y=x;data[num].z=0;data[num].next=h[y];h[y]=num;
}
inline bool bfs(){
    queue<int> q;memset(level,0,sizeof(level));level[0]=1;q.push(0);
    while(!q.empty()){
        int x=q.front();q.pop();
        for (int i=h[x];i;i=data[i].next){
            int y=data[i].y,z=data[i].z;
            if (level[y]||!z) continue;level[y]=level[x]+1;q.push(y);if (y==T) return 1;
        }
    }return 0;
} 
inline int dfs(int x,int s){
    if (x==T) return s;int ss=s;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;
        if (level[x]+1==level[y]&&z){
            int xx=dfs(y,min(s,z));if (!xx) level[y]=0;
            s-=xx;data[i].z-=xx;data[i^1].z+=xx;if (!s) return ss;
        }
    }return ss-s;
}
int main(){
    freopen("3872.in","r",stdin);
    n=read();m=read();T=n+1;int sum=0;
    for (int i=1;i<=n;++i){
        int tmp=read();if (tmp>=0) insert1(0,i,tmp),sum+=tmp;else insert1(i,T,-tmp);
    }
    for (int i=1;i<=m;++i){
        int x=read(),y=read(),z=read();
                if (z<0)continue;
        insert1(x,y,z);
    }int ans=0;while(bfs()) ans+=dfs(0,inf);
    printf("%d",sum-ans);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值