JZOJ 1154. 【GDOI2003】购物

1154. 【GDOI2003】购物 (Standard IO)

Time Limits: 1000 ms Memory Limits: 65536 KB

Description

  GDOI商场推出优惠活动,以超低价出售若干种商品。但是,商场为避免过分亏本,规定某些商品不能同时购买,而且每种超低价商品只能买一件。身为顾客的你想获得最大的实惠,也就是争取节省最多的钱。经过仔细研究,发现商场出售的超低价商品中,不存在以下情况:   n(n>=3)种商品C1,C2,…..,Cn,其中Ci,Ci+1是不能同时购买的(i=1,2…,n-1)并且C1, Cn也不能同时购买。   编程计算可以节省的最大金额数。

Input

  第一行两个整数K,M(1<=K<=1000).其中K表示超低价商品数。K种商品的编号依次为1,2,…,K。M表示不能同时购买的商品对数.接下来K行,第i行有一个整数Xi表示购买编号为i的商品可以节省的金额(1<=Xi<=100).再接下来M行,每行两个数A ,B,表示A和B不能同时购买,1<=A<=K,1<=B<=K,A<>B

Output

  仅一个整数,表示能节省的最大金额数。

Sample Input

3 1 1 1 1 1 2

Sample Output

2

Data Constraint

题解

把所有商品当成点,分两部分看

  1. 没有任何限制的点
  2. 有限制的点,相互限制的点连一条边

很明显,第一部分的点是一定要取的

而第二部分则构成了一棵树(因为没有环),这部分的点有两种解法

  1. 树形dp
    用f[i][0]表示不取第i个点
    用f[i][1]表示取第i个点
    转移就是老爸取了儿子不能取,儿子取了老爸不能取

  2. 二分图
    最优匹配问题,也可以转化为最小割
    求出来的最优匹配或最小割为ans
    则答案是n-ans/2

代码

树形dp

#include<cstdio>
#include<algorithm>
#define N 1001
using namespace std;

long map[N][N],father[N],lc[N],rb[N],ch[N];
long f[N][2];

void build(long now)
{   long i,next;
    for(i=1;i<=map[now][0];i++)if(map[now][i]!=father[now]){
        if(!lc[now])lc[now]=map[now][i];
        else rb[next]=map[now][i];
        father[map[now][i]]=now;
        next=map[now][i];
        ch[now]++;
        build(next);
    }
}
void suan(long now)
{   long q;
    ch[father[now]]--;
    if(!lc[now])
        f[now][0]=0;
    else for(q=lc[now];q;q=rb[q]){
        f[now][0]+=max(f[q][0],f[q][1]);
        f[now][1]+=f[q][0];
    }
}


int main()
{   long n,m,i,j,x,y,ans=0;
    scanf("%ld%ld",&n,&m);
    for(i=1;i<=n;i++)
        scanf("%ld",&f[i][1]);
    for(i=1;i<=m;i++){
        scanf("%ld%ld",&x,&y);
        map[x][++map[x][0]]=y;
        map[y][++map[y][0]]=x;
    }
    for(i=1;i<=n;i++)
        if(map[i][0]&&!father[i])
            build(i);
    for(i=1;i<=n;i++)if(!ch[i]){
        suan(i);
        for(j=father[i];!ch[j]&&j;j=father[j])
            suan(j);
    }
    for(i=1;i<=n;i++)
        if(!father[i])ans+=max(f[i][0],f[i][1]);
    printf("%ld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值