codevs1033

题目地址:http://codevs.cn/problem/1033/

分析:

首先说一下建模, 先把每个点拆成两个, 我们称第一个点为 fi, 第二个点位 se, 这样, 对于每一个点, 我们 由fi -> se 连一条容量为1, 花费为 -点权(注意是负的) 的 边(别忘了反边). 然后 对于每一个点的se 向他可以到达的点的fi连一条容量为1花费为0的边.

最后虚拟两个点 st, ed 由st 向第一层的fi 点连一条容量为1, 花费为0 的边, 由最后一层的se 向ed连一条容量为1 花费0 的边.

这样建模就完了.

然后做k次(当然如果没有增广路可以break) 增广算法, 求 最小费用流,  然后输出 相反数就可以AC了

代码:

#include<cstdio>
#include<cstring>
#include<deque>
using namespace std;
#define N 38
#define INF 0x7fffffff
int n, m, k;
int map[N][N];
int dis[N * N * 2];
int incf[N * N * 2], lin[N * N * 2];
bool v[N * N * 2];
deque <int> Q;
struct arr{
     int ff, tt, ww, next, cost;
}c[N * N * 2 * N * N];
int st = 0, ed;
int r[N * N * 2];
int inc;
int maxcost = 0;
int tot = 1;
inline void add(int x, int y, int z, int cost){
       c[++tot].ff = x; c[tot].tt = y; c[tot].ww = z; c[tot].cost = cost; c[tot].next = r[x]; r[x] = tot;
      c[++tot].ff = y; c[tot].tt = x; c[tot].ww = 0; c[tot].cost = -cost;c[tot].next = r[y]; r[y] = tot;
}

bool spfa(){
      memset(dis, 0x3f, sizeof(dis));
      dis[st] = 0;
    Q.clear();
      Q.push_back(st);
    v[st] = 1;
    incf[st] = INF;
    while (Q.size()){
         int x = Q.front();
         Q.pop_front();
         v[x] = 0;
         for (int i = r[x]; i; i = c[i].next){
              int y = c[i].tt;
             if (dis[y] > dis[x] + c[i].cost && c[i].ww > 0){
                 dis[y] = dis[x] + c[i].cost;
                  incf[y] = min(incf[x], c[i].ww);
                lin[y] = i;
                 if (!v[y]){
                     v[y] = 1;
                     if (dis[y] < dis[x]) Q.push_front(y);
                      else Q.push_back(y);
                 }
             }
        }
      }
      return dis[ed] != 0x3f3f3f3f;
}
inline int num(int x, int y){
     x --;
     return (x) * m + ((x * x - x) >> 1) + y;
}
void EK(){
   int x = ed, y;
   while (lin[x]){
   y = x; x = c[lin[x]].ff;
   c[lin[y]].ww -= incf[ed];
   c[lin[y] ^ 1].ww += incf[ed];
   }
     maxcost += incf[ed] * dis[ed];
     return ;
}


int main(){
freopen("1.in", "r", stdin);
freopen("1.out", "w", stdout);
scanf("%d %d %d",&n, &m, &k);
    int nn = n;
    ed = ((nn * m + ((n * n - n) >> 1)) << 1) + 1;
    inc = nn * m + ((n * n - n) >> 1);
    for (int i = 1; i <= n; i ++){
          for (int j = 1; j <= m + i - 1; j ++){
              scanf("%d",&map[i][j]);
              add(num(i, j), num(i, j) + inc, 1, - map[i][j]);
      }
    }
    for (int i = 1; i < n; i ++)
         for (int j = 1; j <= m + i - 1; j ++){
             add(num(i, j) + inc, num(i + 1,j), 1, 0);
            add(num(i, j) + inc, num(i + 1, j + 1), 1, 0);
    }
      for (int i = 1; i <= m; i ++){
          add(st, num(1, i), 1, 0);
       }
      for (int i = 1; i <= n + m - 1; i ++){
          add(num(n, i) + inc, ed, 1, 0);
        }
      for (int i = 1; i <= k; i ++){
          if (!spfa()) break;
          EK();
      }
      printf("%d\n", -maxcost);
        return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值