2019 Multi-University Training Contest 3 K Subsequence hdu6611 (费用流,dij优化)

K Subsequence

Time Limit: 2000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1992    Accepted Submission(s): 457


 

Problem Description

Master QWsin is dating with Sindar. And now they are in a restaurant, the restaurant has n dishes in order. For each dish, it has a delicious value ai. However, they can only order k times. QWsin and Sindar have a special ordering method, because they believe that by this way they can get maximum happiness value.

Specifically, for each order, they will choose a subsequence of dishes and in this subsequence, when i<j, the subsequence must satisfies ai≤aj. After a round, they will get the sum of the subsequence and they can't choose these dishes again. 

Now, master QWsin wants to know the maximum happiness value they can get but he thinks it's too easy, so he gives the problem to you. Can you answer his question?

 

 

Input

There are multiple test cases. The first line of the input contains an integer T, indicating the number of test cases. For each test case:

First line contains two positive integers n and k which are separated by spaces.

Second line contains n positive integer a1,a2,a3...an represent the delicious value of each dish.


1≤T≤5

1≤n≤2000

1≤k≤10

1≤ai≤1e5

 

 

Output

Only an integer represent the maximum happiness value they can get.

 

 

Sample Input

 

1 9 2 5 3 2 1 4 2 1 4 6

 

 

Sample Output

 

22

 

 

Source

2019 Multi-University Training Contest 3

 思路:我们可以根据题意得知,我们需要求k次不下降子序列(所有子序列中元素不重复),并使k次不下降子序列的总和最大。

我们可以考虑用费用流来做,进行k次增广,求最大费用流。下面考虑建图:

首先需要将起点分成s0,s1,并建立一条s0到s1,流量为k,费用为0的边,保证可以进行k次寻找。

然后需要将每个点分成入点和出点,并且在他们之间建立流量为1,费用为-v[i]的边,保证每个点只会被选取一次,并贡献v[i]。

然后需要将s1与每个点的入点连接,建立流量为1,费用为0的边。同理将每个点的出点与终点t连接,建立流量1,费用0的边。

之后根据题目要求,将所有i<j且v[i]<=v[j]的点之间,用流量为1,费用为0的边连接他们的出点与入点。

然后就可以利用费用流求解了。

注意:出题人卡了SPFA,需要用dij来求最长路。

AC代码:


#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;++i)
using namespace std;

const int MAXN = 5e3+50;
const int INF = 0x3f3f3f3f;
typedef pair<int, int> P;
struct edge{int to, cap, cost, rev;};
int n;                          //顶点数
vector<edge> G[MAXN];           //图的邻接表表示
int h[MAXN];                    //顶点的势
int dist[MAXN];                 //最短距离
int prevv[MAXN], preve[MAXN];   //最短路中的前驱结点和对应的边

//优化输入
template <class T>
inline bool scan_d(T &ret) {
	char c; int sgn;
	if (c = getchar(), c == EOF) return 0;
	while (c != '-' && (c<'0' || c>'9')) c = getchar();
	sgn = (c == '-') ? -1 : 1;
	ret = (c == '-') ? 0 : (c - '0');
	while (c = getchar(), c >= '0'&&c <= '9') ret = ret * 10 + (c - '0');
	ret *= sgn;
	return 1;
}

//向图中增加一条从from到to容量为cap费用为cost的边
void add_edge(int from, int to, int cap, int cost){
    G[from].push_back((edge){to, cap, cost, G[to].size()});
    G[to].push_back((edge){from, 0, -cost, G[from].size()-1});
}

//求解从s到t流量为f的最小费用流
//如果没有流量为f的流,则返回-1
//当f为0时,是流量为f的最小费用
//当f大于0时,是最大流的最小费用
int min_cost_flow(int s, int t, int &f){
    int res = 0;
    fill(h, h+n+1,0); //初始化h
    while(f>0){
        //使用Dijstra算法更新h
        priority_queue<P, vector<P>, greater<P> >que;
        fill(dist, dist+n+1, INF);
        dist[s] = 0;
        que.push(P(0, s));
        while(!que.empty()){
            P p = que.top(); que.pop();
            int v = p.second;
            if(dist[v]<p.first) continue;
            for(int i = 0; i < G[v].size(); i++){
                edge &e = G[v][i];
                if(e.cap > 0 && dist[e.to] > dist[v] + e.cost + h[v] - h[e.to]){
                    dist[e.to] = dist[v] + e.cost + h[v] - h[e.to];
                    prevv[e.to] = v;
                    preve[e.to] = i;
                    que.push(P(dist[e.to], e.to));
                }
            }
        }
        if(dist[t] == INF){
            return res;
        }
        for(int v = 1; v <= n; v++) h[v] += dist[v];

        int d = f;
        for(int v = t; v != s; v = prevv[v]){
            d = min(d, G[prevv[v]][preve[v]].cap);
        }
        f -= d;
        res += d*h[t];
        for(int v = t; v != s; v = prevv[v]){
            edge &e = G[prevv[v]][preve[v]];
            e.cap -= d;
            G[v][e.rev].cap += d;
        }
    }
    return res;
}
int arr[2050];
int main() {
	//ios::sync_with_stdio(false);
	int m, k, T;
	scanf("%d", &T);
    while(T--){
        scanf("%d%d", &m, &k);
        rep(i, 1, m){
            scanf("%d", &arr[i]);
        }
        int s0 = 2*(m+1), s1 = 2*(m+1)+1;
        int t = 1;
        add_edge(s0,s1,k,0);
        rep(i, 1, m){
            add_edge(s1, 2*i, 1, 0);
            add_edge(2*i, 2*i+1, 1, -arr[i]);
            add_edge(2*i+1, t, 1, 0);
        }
        rep(i, 1, m){
            rep(j, 1, m){
                if(i < j && arr[i] <= arr[j]){
                    add_edge(2*i+1, 2*j, 1, 0);
                }
            }
        }
        n = 2*(m+1)+1;
        int mxflow = INF;
        int ans = min_cost_flow(s0, t, mxflow);
        printf("%d\n", -ans);
        rep(i, 1, n){
            G[i].clear();
        }
    }
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值