bzoj3158 千钧一发

10 篇文章 0 订阅
3 篇文章 0 订阅

bzoj3158 千钧一发

Description
1
Input

第一行一个正整数N。

第二行共包括N个正整数,第 个正整数表示Ai。

第三行共包括N个正整数,第 个正整数表示Bi。

Output

共一行,包括一个正整数,表示在合法的选择条件下,可以获得的能量值总和的最大值。

Sample Input

4
3 4 5 12
9 8 30 9

Sample Output

39

HINT

1<=N<=1000,1<=Ai,Bi<=10^6

这道题也是一道很好的题啊.. 值得去想一想

因为题目要求我们求最大值,那我们就想,能不能把它转化成总和减去损失的最小值呢?对了,就是最小割

首先我们先想一下一个割边的模型,也就是每个点连源连汇,然后割掉其中一条边表示选这个点进入集合所损失的价值,割掉另一条边表示不选这个点进入集合所损失的价值

又由于两个点是会因为某些条件不能在同一个集合中,所以我们要这样建边:
如下:
这里写图片描述

请注意,中间这一条边是单向边而不是各位认为的双向边

假如两个点都割【选】的边,那么肯定会存在有某个点割【不选】的边使得这种方案的最小割比当前的优,因为存在一条【不选】->【inf】->【不选】的增广路。但是,如果我现在割掉两个【不选】的边,并没有什么关系啊..

那么你也许会问,这个东西保证是二分图吗。那我下面给出证明咯
1.假如两个点都为偶数,那么他们满足第二个条件
2.加入两个点都为奇数,那么他们满足第一个条件
至于为什么,自己好好想想

所以就酱紫咯..

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cmath>
#define LL long long
using namespace std;
const int Maxn = 1100;
const int inf = 0x7fffffff;
struct node {
    int x, y, next, c, opp;
}a[Maxn*Maxn*5]; int first[Maxn], len;
int _min ( int x, int y ){ return x < y ? x : y; }
void ins ( int x, int y, int c ){
    len ++; int k1 = len;
    a[len].x = x; a[len].y = y; a[len].c = c;
    a[len].next = first[x]; first[x] = len;
    len ++; int k2 = len;
    a[len].x = y; a[len].y = x; a[len].c = 0;
    a[len].next = first[y]; first[y] = len;
    a[k1].opp = k2;
    a[k2].opp = k1;
}
int st, ed, h[Maxn];
int n;
int na[Maxn];
int gcd ( int a, int b ){
    if ( a == 0 ) return b;
    return gcd ( b%a, a );
}
bool check ( int x, int y ){
    LL ph = (LL)x*x+(LL)y*y;
    LL kf = sqrt (ph);
    if ( kf*kf != ph ) return false;
    if ( gcd ( x, y ) == 1 ) return true;
    else return false;
}
bool bfs (){
    queue <int> q;
    memset ( h, -1, sizeof (h) );
    q.push (st); h[st] = 0;
    while ( !q.empty () ){
        int x = q.front (); q.pop ();
        for ( int k = first[x]; k; k = a[k].next ){
            int y = a[k].y;
            if ( h[y] == -1 && a[k].c > 0 ){
                h[y] = h[x]+1;
                q.push (y);
            }
        }
    }
    return h[ed] > 0;
}
int dfs ( int x, int flow ){
    if ( x == ed ) return flow;
    int delta = 0;
    for ( int k = first[x]; k; k = a[k].next ){
        int y = a[k].y;
        if ( h[y] == h[x]+1 && a[k].c > 0 && flow-delta > 0 ){
            int minf = dfs ( y, _min ( a[k].c, flow-delta ) );
            delta += minf;
            a[k].c -= minf;
            a[a[k].opp].c += minf;
        }
    }
    if ( delta == 0 ) h[x] = -1;
    return delta;
}
int main (){
    int i, j, k;
    scanf ( "%d", &n );
    for ( i = 1; i <= n; i ++ ) scanf ( "%d", &na[i] );
    len = 0; memset ( first, 0, sizeof (first) );
    st = 0; ed = n+1;
    int sum = 0;
    for ( i = 1; i <= n; i ++ ){
        int x;
        scanf ( "%d", &x );
        if ( na[i] % 2 == 1 ) ins ( st, i, x );
        else ins ( i, ed, x );
        sum += x;
    }
    for ( i = 1; i < n; i ++ ){
        for ( j = i+1; j <= n; j ++ ){
            if ( check ( na[i], na[j] ) == true ){
                if ( na[i] % 2 == 0 ) ins ( j, i, inf );
                else ins ( i, j, inf );
            }
        }
    }
    int ans = 0, delta;
    while ( bfs () ){
        while ( delta = dfs ( st, inf ) ) ans += delta;
    }
    printf ( "%d\n", sum-ans );
    return 0;
}

记住,在进行平方操作时,请注意 1Ai,Bi106 ,所以会爆int.. 我就因为这个错了两次啊..

论对拍的重要性啊..

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值