bzoj3158 千钧一发

bzoj3158 千钧一发

Description

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

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

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

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

又由于两个点是会因为某些条件不能在同一个集合中,所以我们要这样建边:
如下:
bzoj3158 千钧一发_第1张图片

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

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

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

所以就酱紫咯..

#include 
#include 
#include 
#include 
#include 
#include 
#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.. 我就因为这个错了两次啊..

论对拍的重要性啊..

你可能感兴趣的:(bzoj,网络流,最小割)