【BZOJ-1952】Area [坑题] 仙人掌DP + 最大点权独立集(改)

1952: [Sdoi2010]Area

Time Limit: 10 Sec  Memory Limit: 128 MB
Submit:
73  Solved: 23
[Submit][Status][Discuss]

Description

小猪iPig来到了一个叫做pigsty的城市里,pigsty是一座专门为小猪所准备的城市,城市里面一共有n个小区给小猪们居住,并且存在许多条无向边连接着许多小区。因为这里是一个和谐的城市,所以小猪iPig准备在这个城市里面度过他的余生。若干年之后小猪iPig当上了规划局长,这件事令他非常开心。不过与此同时pigsty城市里面出现了许多反和谐主义者,他们已经厌烦了这样和谐的生活,在城市里到处闹事。小猪iPig为了更好地控制局面,他把城市改造成了另外一个样子:iPig把道路全部摧毁之后重新修建了m条无向边,并且保证每一个小区最多存在于一个由无向边组成的环中。
iPig以为这样做就让那些反和谐主义者不敢继续猖狂下去了,谁知到在新的城市道路修建好以后反和谐主义者宣言要对城市的小区进行一次洗脑!这下可麻烦了,iPig赶紧收集了许多的情报。iPig给每个小区标记了一个和谐值HX_i,用它来表示第i个小区的和谐程度。通过地下消息iPig又得知那些反和谐主义者进攻时有个规律:他们会选择若干个小区下手,这些小区都派一只猪过去,把这些小区的和谐值归零。在这个过程中,每个选择的小区所直接连接着的几个小区都派了一只猪去看守——以防被警猪给干扰。这个计划看似完美但是还是存在一个漏洞:因为人员之间都是在网络上认识的,互相没有见过面,为了防止不必要的麻烦(认错猪之类),每个小区最多只会有一头猪存在。
iPig突然感到了莫大的压力,他想知道在最坏情况下会丢失多少和谐值。但是不懂计算机的他不知道应该怎样计算。你能帮帮他吗?

Input

输入第一行有两个整数n和m,表示pigsty城市里面有n个小区,在iPig修整城市后有m条无向边连接着n个小区。接下来一行有n个正整数,第i个正整数HX_i表示第i个小区的和谐值为HX_i。接下来m行,每行两个正整数a和b(1<=a,b<=n),表示存在一条连接着小区a和小区
b的无向边。

Output

输出只有一行一个整数,表示最坏情况下损失的和谐值为多少。

Sample Input

9 9
2 2 3 4 1 2 3 10 11
1 2
2
3
1 3
3 5
5 4
5 6
4 7
6 7
8 9

Sample Output

17

【样例解释】
反和谐主义者选择的小区分别是小区3(看守的小区是小区1、小区2和小区5)、小区7(看守的小区是小区4和小区6)和小区9(看守的小区是小区8),这样会损失的总和谐值为3+3+11=17。
或者选择的小区分别是小区1(看守的小区是小区2和小区3)、小区4(看守的小区是小区5和小区7)和小区9(看守的小区是小区8),这样会损失的总和谐值为2+4+11=17。
如果同时选择小区3、小区4和小区9,虽然损失的总和谐值为18,但是小区3和小区4都要派猪来看守小区5,这不符合条件,故此方案不可行。

【数据约定】
对于20%的数据,保证每个点不存在于任何一个环中;
对于另外30%的数据,保证图中只存在一个环;
对于100%的数据,有N<=1000000,M<=2000000,所有的权值不超过1000。

HINT

Source

Solution

这是一道坑题,测试数据有误,标算有误,所以想要AC需要和标算错的一样 详见Discuss

询问了AC此题的Claris,得到的回复是:

说说正确做法:

仍旧是仙人掌DP,这里的话是仙人掌森林,不过原理是一样的e

求解最大点权独立集,然后仔细读样例发现,这里的“独立集”不同于平常的独立集,即不能选中间隔着一个的两个点

那么对于正常的求解方法是dp[i][0/1]表示当前到i位,选或不选的答案,这里就带限制的dp[i][0/1/2]去进行dp即可,转移是类似的

对仙人掌的处理方法一样是找环,拆环单独DP

Code

这里提供自己的正确做法(WA3组(数据有误)AC其余)

除discuss中提及的3组全A,并与提及的3组的正确答案相吻合

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 1000100
struct EdgeNode{int to,next;}edge[maxn<<2];
int head[maxn],cnt;
void add(int u,int v) {cnt++; edge[cnt].next=head[u]; head[u]=cnt; edge[cnt].to=v;}
void insert(int u,int v) {add(u,v); add(v,u);}
int deep[maxn],fa[maxn],dfn[maxn],low[maxn],dp1[maxn][3],dp2[maxn][3],ring[maxn],HX[maxn],t;
int n,m,ans;

void CactusDP(int st,int tt)
{
    ring[1]=tt; int zz=1;
    while (ring[zz]!=st) {ring[zz+1]=fa[ring[zz]]; zz++;}
    //printf("Num=%d  :",zz);
    //for (int i=1; i<=zz; i++) printf("%d ->",ring[i]); printf("\n");
    int f0=0,f1=0,f2=0;
    for (int opt=0; opt<=2; opt++)
        {
            dp2[1][0]=dp2[1][1]=dp2[1][2]=0;
            dp2[1][opt]=dp1[tt][opt];
            if (opt==2) dp2[1][1]=dp2[1][2];
            for (int i=2; i<=zz; i++)
                dp2[i][0]=dp2[i-1][2]+dp1[ring[i]][0],
                dp2[i][1]=max(max(dp2[i-1][1],dp2[i-1][0])+dp1[ring[i]][2],dp2[i-1][1]+dp1[ring[i]][1]),
                dp2[i][2]=max(dp2[i-1][1],dp2[i-1][2])+dp1[ring[i]][2];
            if (opt==0) f1=max(f1,dp2[zz][2]);
            if (opt==1) f1=max(f1,dp2[zz][1]),f2=max(f2,dp2[zz][2]);
            if (opt==2) f1=max(f1,dp2[zz][1]),f0=max(f0,dp2[zz][0]),f2=max(f2,dp2[zz][2]);
        }
    dp1[st][0]=f0; dp1[st][1]=f1; dp1[st][2]=f2;
}

void TreeDP(int now)
{
    dfn[now]=low[now]=++t;
    dp1[now][2]=0; dp1[now][0]=HX[now]; int maxx=0;
    for (int i=head[now]; i; i=edge[i].next)
        if (edge[i].to!=fa[now])
            {
                if (deep[edge[i].to]) {low[now]=min(dfn[edge[i].to],low[now]); continue;}
                fa[edge[i].to]=now; deep[edge[i].to]=deep[now]+1;
                TreeDP(edge[i].to);
                if (low[edge[i].to]>low[now])
                    dp1[now][2]+=max(dp1[edge[i].to][1],dp1[edge[i].to][2]),
                    dp1[now][0]+=dp1[edge[i].to][2],
                    maxx=max(maxx,dp1[edge[i].to][0]-max(dp1[edge[i].to][1],dp1[edge[i].to][2]));
                low[now]=min(low[now],low[edge[i].to]);
            }
    dp1[now][1]=maxx+dp1[now][2];
    for (int i=head[now]; i; i=edge[i].next)
        if (low[edge[i].to]==dfn[now] && edge[i].to!=fa[now] && deep[edge[i].to]!=deep[now]+1)
            CactusDP(now,edge[i].to);
}

void Freopen() {freopen("area.in","r",stdin); freopen("area.out","w",stdout);}
void Fclose() {fclose(stdin); fclose(stdout);}

int main()
{
    //Freopen();
    n=read(),m=read();
    for (int i=1; i<=n; i++) HX[i]=read();
    for (int u,v,i=1; i<=m; i++) u=read(),v=read(),insert(u,v);
    for (int i=1; i<=n; i++) if (!dfn[i]) {fa[i]=i,deep[i]=1; TreeDP(i); ans+=max(dp1[i][0],max(dp1[i][1],dp1[i][2]));}
    printf("%d\n",ans);
    //Fclose();
    return 0;
}

以及可A的标程,显然标程的判环是有些问题的

std

开荒做这种大坑,简直了= =||

时间: 2024-08-05 15:20:56

【BZOJ-1952】Area [坑题] 仙人掌DP + 最大点权独立集(改)的相关文章

HDU 1565 1569 方格取数(最大点权独立集)

HDU 1565 1569 方格取数(最大点权独立集) 题目链接 题意:中文题 思路:最大点权独立集 = 总权值 - 最小割 = 总权值 - 最大流 那么原图周围不能连边,那么就能够分成黑白棋盘.源点连向黑点.白点连向汇点,容量都为点容量.然后黑白之间相邻的就连一条容量无限大的边 代码: #include <cstdio> #include <cstring> #include <queue> #include <algorithm> using names

LiberOJ #6007. 「网络流 24 题」方格取数 最小割 最大点权独立集 最大流

#6007. 「网络流 24 题」方格取数 内存限制:256 MiB时间限制:1000 ms标准输入输出 题目类型:传统评测方式:文本比较 上传者: 匿名 提交提交记录统计讨论测试数据 题目描述 在一个有 m×n m \times nm×n 个方格的棋盘中,每个方格中有一个正整数. 现要从方格中取数,使任意 2 22 个数所在方格没有公共边,且取出的数的总和最大.试设计一个满足要求的取数算法. 输入格式 文件第 1 11 行有 2 22 个正整数 m mm 和 n nn,分别表示棋盘的行数和列数

hdu 1565 方格取数(2)(网络流之最大点权独立集)

题目链接:hdu 1565 方格取数(2) 题意: 有一个n*m的方格,每个方格有一个数,现在让你选一些数.使得和最大. 选的数不能有相邻的. 题解: 我们知道对于普通二分图来说,最大独立点集 + 最小点覆盖集 = 总点数,类似的,对于有权的二分图来说,有: 最大点权独立集 + 最小点权覆盖集 = 总点权和, 这个题很明显是要求 最大点权独立集 ,现在 总点权 已知,我们只要求出来 最小点权覆盖集 就好了,我们可以这样建图, 1,对矩阵中的点进行黑白着色(相邻的点颜色不同),从源点向黑色的点连一

HDU 1569 - 方格取数(2) - [最大点权独立集与最小点权覆盖集]

嗯,这是关于最大点权独立集与最小点权覆盖集的姿势,很简单对吧,然后开始看题. HDU1569: Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,

hdu1565方格取数(1)【最大流||最大点权独立集】

Problem Description 给你一个n*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取的数所在的2个格子不能相邻,并且取出的数的和最大. Input 包括多个测试实例,每个测试实例包括一个整数n 和n*n个非负数(n<=20) Output 对于每个测试实例,输出可能取得的最大的和 Sample Input 3 75 15 21 75 15 28 34 70 5 Sample Output 188 预备知识: 对于一个无向

hdoj 1569 方格取数 【最大点权独立集-&gt;最大流】

题目:hdoj 1569 方格取数 题意:中文题目,就不说题意了. 分类:最大流 |  dp 分析:dp的话应该是个数塔模型,不难做,这里讲转化为图的做法. 这个题目的关键在于转化为一个二分图,来求一个二分图的最大点权独立集,而最大点权独立集 = 点权和 - 最小点权覆盖 最小点权覆盖: 从x或者y集合中选取一些点,使这些点覆盖所有的边,并且选出来的点的权值尽可能小. 最大点权独立集:找到二分图中权值和最大的点集,然后让任意点没有边. 而最小点权覆盖 = 最小割 = 最大流 = sum - 最大

Educational Codeforces Round 21 F. Card Game(网络流之最大点权独立集)

题目链接:Educational Codeforces Round 21 F. Card Game 题意: 有n个卡片,每个卡片有三个值:p,c,l; 现在让你找一个最小的L,使得满足选出来的卡片l<=L,并且所有卡片的p的和不小于k. 选择卡片时有限制,任意两张卡片的c之和不能为质数. 题解: 和hdu 1565 方格取数(2)一样,都是求最大点权独立集. 不难看出来,这题再多一个二分. 注意的是在构造二部图的时候,按照c值的奇偶性构造. 当c==1时要单独处理,因为如果有多个c==1的卡片,

hdu 1569 方格取数(2) 最大点权独立集

方格取数(2) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 5425    Accepted Submission(s): 1695 Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的

HDU1569 最大流(最大点权独立集)

方格取数(2) Time Limit: 10000/5000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 68 Accepted Submission(s): 33   Problem Description 给你一个m*n的格子的棋盘,每个格子里面有一个非负数.从中取出若干个数,使得任意的两个数所在的格子没有公共边,就是说所取数所在的2个格子不能相邻,并且取出的数的和最大. In