【洛谷 P4134】 [BJOI2012]连连看(费用流)

题目链接
首先是可以\(O(n^2)\)枚举出所有符合要求的点对的,然后考虑建图。
还是拆点把每个点拆成入点和出点,源点连入点,出点连汇点,流量都是1,费用都是0。
然后对于没对符合要求的\((x,y)\),连接\((x_{in},y_{out}),(y_{in},x_{out})\),费用均为\(x+y\),流量均为\(1\)
然后跑出最大费用最大流,最大流除以2就是第一问,最大费用除以2就是第二问。

为什么要双向连边然后答案除以2?单向连边去试试就知道了
PS:会重复。

#include <cstdio>
#include <queue>
#include <cmath>
#include <cstring>
#define INF 2147483647
using namespace std;
const int MAXN = 5010;
const int MAXM = 200010;
queue <int> q; 
int s, t, now, n, m;
struct Edge{
    int from, next, to, rest, cost;
}e[MAXM];
int head[MAXN], num = 1, dis[MAXN], vis[MAXN], Flow[MAXN], pre[MAXN];
inline void Add(int from, int to, int flow, int cost){
    e[++num] = (Edge){ from, head[from], to, flow, cost }; head[from] = num;
    e[++num] = (Edge){ to, head[to], from, 0, -cost }; head[to] = num;
}
int RoadsExist(){
    q.push(s);
    memset(dis, 127, sizeof dis);
    dis[s] = 0; Flow[s] = INF; pre[t] = 0;
    while(!q.empty()){
      now = q.front(); q.pop(); vis[now] = 0;
      for(int i = head[now]; i; i = e[i].next)
         if(e[i].rest && dis[e[i].to] > dis[now] + e[i].cost){
           dis[e[i].to] = dis[now] + e[i].cost;
           pre[e[i].to] = i;
           Flow[e[i].to] = min(Flow[now], e[i].rest);
           if(!vis[e[i].to]){
             vis[e[i].to] = 1;
             q.push(e[i].to);
           }
         }
    }
    return pre[t];
}
int a, b, c, d, maxflow, mincost;
int gcd(int a, int b){
    return b ? gcd(b, a % b) : a;
}
int main(){
    scanf("%d%d", &a, &b); s = 4999; t = 5000;
    for(int i = a; i <= b; ++i){
       Add(s, i, 1, 0);
       Add(i + 1000, t, 1, 0);
       for(int j = a; j < i; ++j){
          c = i * i - j * j;
          d = sqrt(c);
          if(d * d == c && gcd(d, j) == 1){
            Add(i, j + 1000, 1, -i - j);
            Add(j, i + 1000, 1, -i - j);
          }
       }
    }
    while(RoadsExist()){
      maxflow += Flow[t];
      mincost += Flow[t] * dis[t];
      for(int i = t; i != s; i = e[pre[i]].from){
         e[pre[i]].rest -= Flow[t];
         e[pre[i] ^ 1].rest += Flow[t];
      }
    }
    printf("%d %d\n", maxflow >> 1, -mincost >> 1);
    return 0;
}

转载于:https://www.cnblogs.com/Qihoo360/p/10500109.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值