Codeforces 863F

863F - Almost Permutation

题意

给出每个位置可以放的数字的范围,定义 (cost = sum_{i=1}^{n}(cnt(i))^2) ,其中 (cnt(i)) 为数字 (i) ((1 leq i leq n)) 出现的次数。将每个位置都填上一个数字,求 (cost) 的最小值。

分析

没想到可以用网络流去解决这道问题。本题属于最小费用最大流问题。
对于每个代表数字的结点,从源点都要连 (n) 条边,费用为(1, 3, 5,..., 2*k-1),前缀和正好对应某个数字取 (k) 次时的花费。根据位置和可以放的数字之间的对应关系连边,费用为 (0) ,所有代表位置的结点连边到汇点,费用为 (0)。以上每条边的容量都为 (1) 。跑一下费用流的模板即可。

思维好题啊。

code

#include<bits/stdc++.h>
#define l first
#define r second
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int MAXN = 1005; //
const int MAXM = 10005; // 注意扩张的边的数量
const int INF = 0x3f3f3f3f;
struct Edge {
    int to, next, cap, flow, cost;
} edge[MAXM * 4];
int head[MAXN], tol;
int pre[MAXN], dis[MAXN];
bool vis[MAXN];
int N;//节点总个数,节点编号从0~N-1
void init(int n) {
    N = n;
    tol = 0;
    memset(head, -1, sizeof head);
    memset(pre, 0, sizeof pre);
    memset(dis, 0, sizeof dis);
    memset(vis, 0, sizeof vis);
    memset(edge, 0, sizeof edge);
}
void addedge (int u, int v, int cap, int cost) {
    edge[tol] = Edge{v, head[u], cap, 0, cost};
    head[u] = tol++;
    edge[tol] = Edge{u, head[v], 0, 0, -cost};
    head[v] = tol++;
}
bool spfa(int s, int t) {
    queue<int>q;
    for(int i = 0; i < N; i++) {
        dis[i] = INF;
        vis[i] = false;
        pre[i] = -1;
    }
    dis[s] = 0;
    vis[s] = true;
    q.push(s);
    while(!q.empty()) {
        int u = q.front();
        q.pop();
        vis[u] = false;
        for(int i = head[u]; i != -1; i = edge[i].next) {
            int v = edge[i]. to;
            if(edge[i].cap > edge[i].flow &&
                    dis[v] > dis[u] + edge[i].cost) {
                dis[v] = dis[u] + edge[i].cost;
                pre[v] = i;
                if(!vis[v]) {
                    vis[v] = true;
                    q.push(v);
                }
            }
        }
    }
    if(pre[t] == -1) return false;
    else return true;
}
//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s, int t, int &cost) {
    int flow = 0;
    cost = 0;
    while(spfa(s,t)) {
        int Min = INF;
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            if(Min > edge[i].cap - edge[i]. flow)
                Min = edge[i].cap - edge[i].flow;
        }
        for(int i = pre[t]; i != -1; i = pre[edge[i^1].to]) {
            edge[i].flow += Min;
            edge[i^1].flow -= Min;
            cost += edge[i].cost * Min;
        }
        flow += Min;
    }
    return flow;
}
P a[110];
int main() {
    int n, q;
    cin >> n >> q;
    for(int i = 1; i <= n; i++) {
        a[i].l = 1;
        a[i].r = n;
    }
    int flg = 0;
    while(q--) {
        int op, l, r, v;
        cin >> op >> l >> r >> v;
        for(int i = l; i <= r; i++) {
            if(op == 1) {
                a[i].l = max(a[i].l, v);
            } else {
                a[i].r = min(a[i].r, v);
            }
            if(a[i].l > a[i].r) {
                flg = 1;
                break;
            }
        }
    }
    init(2 * n + 2);
    int s = 0, t = 2 * n + 1;
    for(int i = 1; i <= n; i++) {
        for(int j = 1; j <= n; j++) { // 源点向每个代表数字的结点连 n 条边
            addedge(s, i, 1, 1 + 2 * (j - 1));
        }
        for(int j = a[i].l; j <= a[i].r; j++) { // 根据数字和位置的对应关系连边
            addedge(j, i + n, 1, 0);
        }
        addedge(i + n, t, 1, 0); // 代表位置的结点向汇点连边
    }
    int cost = 0;
    int flow = minCostMaxflow(s, t, cost);
    if(flow != n) cost = -1;
    cout << cost << endl;
}
原文地址:https://www.cnblogs.com/ftae/p/7614536.html