HDU 4106

有很多种写法,不过基本大同小异

不过记得两年前自己居然写了让自己现在诡异所思的代码

建图一:

最小费用最大流:n个点拆成n-m+1个区间,每两个相邻区间之间连边,权值为0,流量为k

对于每一个点,能包括它的最左边的区间向这个区间无交集的下一个区间连一条边,权值为这个点的负权值,流量为1

大致思想就是样,因为一个区间最多能被切k次,所以切完k次后的每一次流量都要流到跟这个区间完全没有交集的区间去

采用负权值,求出最小费用最大流后费用取反就是答案了

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <queue>
  7 #include <vector>
  8 #include <string>
  9
 10 using namespace std;
 11
 12 typedef long long LL;
 13 typedef pair <int, int> PII;
 14
 15 const int N = 1e3 + 7;
 16 const int M = N * N * 4;
 17 const int INF = 0x3f3f3f3f;
 18 const int MOD = 1e9 + 7;
 19 const double EPS = 1e-12;
 20
 21 struct edge{
 22     int x, ne, c, f, w;
 23 };
 24
 25 struct MinCostFlow{
 26     edge e[M];
 27     int S, T, pos, quantity, cost;
 28     int head[N << 1], dis[N << 1], pre[N << 1], at[N << 1];
 29     queue <int> q;
 30     bool used[N << 1];
 31
 32     void adde(int u, int v, int c, int w){
 33         //printf("Add edge : %d -> %d c : %d w : %d\n", u, v, c, w);
 34         e[++pos] = (edge){v, head[u], c, 0, w};
 35         head[u] = pos;
 36         e[++pos] = (edge){u, head[v], c, c, -w};
 37         head[v] = pos;
 38     }
 39
 40     bool spfa(){
 41         memset(dis, 0x3f, sizeof(dis));
 42         memset(used, 0, sizeof(used));
 43         used[S] = true;
 44         while (!q.empty())
 45             q.pop();
 46         q.push(S);
 47         dis[S] = 0;
 48         while (!q.empty()){
 49             int x = q.front();
 50             //printf("Now : %d\n", x);
 51             for (int i = head[x]; i; i = e[i].ne){
 52                 int y = e[i].x;
 53                 if (e[i].c > e[i].f && dis[x] + e[i].w < dis[y]){
 54                     dis[y] = dis[x] + e[i].w;
 55                     at[y] = i;
 56                     pre[y] = x;
 57                     if (!used[y]){
 58                         used[y] = true;
 59                         q.push(y);
 60                     }
 61                 }
 62             }
 63             used[x] = false;
 64             q.pop();
 65         }
 66         //printf("Spfa : %d\n", dis[T]);
 67         return dis[T] != INF;
 68     }
 69
 70     void update(){
 71         int cut = INF;
 72         for (int i = T; i != S; i = pre[i]){
 73             cut = min(cut, e[at[i]].c - e[at[i]].f);
 74         }
 75         //printf("Cut %d‘s path : %d -> ", T);
 76         for (int i = T; i != S; i = pre[i]){
 77             e[at[i]].f += cut;
 78             e[at[i] ^ 1].f -= cut;
 79             //printf(" - > %d", pre[i]);
 80         }
 81         quantity += cut;
 82         cost += cut * dis[T];
 83         //puts("-------");
 84     }
 85
 86     void init(int s, int t){
 87         S = s;
 88         T = t;
 89         pos = 1;
 90         quantity = cost = 0;
 91         memset(head, 0, sizeof(head));
 92     }
 93
 94     PII work(){
 95         //puts("Starting");
 96         while (spfa())
 97             update();
 98         //printf("%d %d\n", quantity, cost);
 99         return make_pair(quantity, cost);
100     }
101 }flow;
102
103 int main(){
104     int n, m, k, x, S, T;
105     while (scanf("%d%d%d", &n, &m, &k) == 3){
106         if (m > n)
107             m = n;
108         flow.init(S = n - m + 2, T = n - m + 3);
109         for (int i = 1; i <= n - m + 1; ++i)
110             flow.adde(i - 1, i, k, 0);
111         flow.adde(S, 0, k, 0);
112         flow.adde(n - m + 1, T, k, 0);
113         for (int i = 1; i <= n; ++i){
114             scanf("%d", &x);
115             flow.adde(max(0, i - m), min(n - m + 1, i), 1, -x);
116         }
117         PII ans = flow.work();
118         printf("%d\n", -ans.second);
119     }
120
121     return 0;
122 }

第二种建图法,貌似是当年高中某大神秒掉这道题的思路,感觉确实牛逼

点还是和上面一样的建法,边除了流量权值也一样

先定义一个较大值U,比INF小

对于相邻的区间,连接一条容量为U - (m - k),权值为0的边

对于每个点,能包括它的最左边的区间向这个区间无交集的下一个区间连一条边,权值为这个点的权值(注意不是负的了),流量为1

源点对于0这个区间的点加一条U容量,0权值的边,T对最后一个区间加一条U容量,0权值的边

最后的答案就是sum(所有点的权值的和)减去最小费用流的费用

想发和上面那中个人感觉差异挺大的,

对于每个区间,我不切m-k个,而且还是至少不切m-k个,且使不切的水果权值尽可能低

  1 #include <iostream>
  2 #include <algorithm>
  3 #include <cstdio>
  4 #include <cstdlib>
  5 #include <cstring>
  6 #include <cassert>
  7 #include <cmath>
  8 #include <map>
  9 #include <set>
 10 #include <queue>
 11 #include <deque>
 12 #include <vector>
 13 #include <string>
 14
 15 using namespace std;
 16
 17 typedef long long LL;
 18 typedef pair <int, int> PII;
 19
 20 const int N = 1e3 + 7;
 21 const int M = N * 10;
 22 const int INF = 0x3f3f3f3f;
 23 const int U = 0x7ffff;
 24 const int MOD = 1e9 + 7;
 25 const double EPS = 1e-12;
 26 const double PI = acos(0) * 2;
 27
 28 struct edge{
 29     int x, ne, c, f, w;
 30 };
 31
 32 struct MinCostFlow{
 33     edge e[M];
 34     int head[N], at[N], pre[N], dis[N], S, T, cost, flow, pos;
 35     queue <int>Q;
 36     bool used[N];
 37
 38     void push(int u, int v, int c, int w){
 39         //printf("Add edge %d - > %d c %d w %d\n", u, v, c, w);
 40         e[++pos] = (edge){v, head[u], c, 0, w};
 41         head[u] = pos;
 42         e[++pos] = (edge){u, head[v], c, c, -w};
 43         head[v] = pos;
 44     }
 45
 46     void init(int s, int t){
 47         S = s;
 48         T = t;
 49         pos = 1;
 50         cost = flow = 0;
 51         memset(head, 0, sizeof(head));
 52     }
 53
 54     bool spfa(){
 55         memset(dis, 0x3f, sizeof(dis));
 56         memset(used, 0, sizeof(used));
 57         while (!Q.empty())
 58             Q.pop();
 59         used[S] = true;
 60         Q.push(S);
 61         dis[S] = 0;
 62         while (!Q.empty()){
 63             int x = Q.front(), y;
 64             for (int i = head[x]; i; i = e[i].ne)
 65                 if (e[i].c > e[i].f && dis[y = e[i].x] > dis[x] + e[i].w){
 66                     dis[y] = dis[x] + e[i].w;
 67                     pre[y] = x;
 68                     at[y] = i;
 69                     if (!used[y]){
 70                         used[y] = true;
 71                         Q.push(y);
 72                     }
 73                 }
 74             Q.pop();
 75             used[x] = false;
 76         }
 77         return dis[T] != INF;
 78     }
 79
 80     void update(){
 81         int cut = INF;
 82         for (int i = T; i != S; i = pre[i]){
 83             cut = min(cut, e[at[i]].c - e[at[i]].f);
 84         }
 85         //printf("Cut path : \n");
 86         for (int i = T; i != S; i = pre[i]){
 87             e[at[i]].f += cut;
 88             e[at[i] ^ 1].f -= cut;
 89             //printf("%d ", i);
 90         }
 91         //printf("%d\n", S);
 92         //printf("Cut %d %d\n", cut, dis[T]);
 93         flow += cut;
 94         cost += cut * dis[T];
 95         //cout << cost << endl;
 96     }
 97
 98     PII work(){
 99         while (spfa())
100             update();
101         return make_pair(flow, cost);
102     }
103 }flow;
104
105 int main(){
106     int n, m, k, sum, S, T, x;
107     while (scanf("%d%d%d", &n, &m, &k) == 3){
108         if (n < m)
109             m = n;
110         S = n - m + 2;
111         T = n - m + 3;
112         flow.init(S, T);
113         for (int i = 1; i <= n - m + 1; ++i){
114             flow.push(i - 1, i, U - (m - k), 0);
115         }
116         flow.push(S, 0, U, 0);
117         flow.push(n - m + 1, T, U, 0);
118         sum = 0;
119         for (int i = 1; i <= n; ++i){
120             scanf("%d", &x);
121             sum += x;
122             flow.push(max(0, i - m), min(i, n - m + 1), 1, x);
123         }
124         PII ans = flow.work();
125         printf("%d\n", sum - ans.second);
126     }
127
128     return 0;
129 }

附上一道类似的题目poj 3680

HDU 4106

时间: 2024-10-09 00:47:56

HDU 4106的相关文章

HDU 4106 Fruit Ninja 区间k覆盖问题 最小费用流

题目链接:点击打开链接 题意: 给定n长的序列,m ,k 选择一些数使得 选择的数和最大.输出和. 限制:对于任意的区间[i, i+m]中至多有k个数被选. 思路: 白书P367,区间k覆盖问题,把一个区间看成一个点,那么选了一个点就相当于覆盖了m个区间. #include<iostream> #include<stdio.h> #include<string.h> #include<queue> #include<math.h> using n

HDU 6203 ping ping ping [LCA,贪心,DFS序,BIT(树状数组)]

题目链接:[http://acm.hdu.edu.cn/showproblem.php?pid=6203] 题意 :给出一棵树,如果(a,b)路径上有坏点,那么(a,b)之间不联通,给出一些不联通的点对,然后判断最少有多少个坏点. 题解 :求每个点对的LCA,然后根据LCA的深度排序.从LCA最深的点对开始,如果a或者b点已经有点被标记了,那么continue,否者标记(a,b)LCA的子树每个顶点加1. #include<Bits/stdc++.h> using namespace std;

HDU 5542 The Battle of Chibi dp+树状数组

题目:http://acm.hdu.edu.cn/showproblem.php?pid=5542 题意:给你n个数,求其中上升子序列长度为m的个数 可以考虑用dp[i][j]表示以a[i]结尾的长度为j的上升子序列有多少 裸的dp是o(n2m) 所以需要优化 我们可以发现dp的第3维是找比它小的数,那么就可以用树状数组来找 这样就可以降低复杂度 #include<iostream> #include<cstdio> #include<cstring> #include

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

[hdu 2102]bfs+注意INF

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2102 感觉这个题非常水,结果一直WA,最后发现居然是0x3f3f3f3f不够大导致的--把INF改成INF+INF就过了. #include<bits/stdc++.h> using namespace std; bool vis[2][15][15]; char s[2][15][15]; const int INF=0x3f3f3f3f; const int fx[]={0,0,1,-1};

HDU 3555 Bomb (数位DP)

数位dp,主要用来解决统计满足某类特殊关系或有某些特点的区间内的数的个数,它是按位来进行计数统计的,可以保存子状态,速度较快.数位dp做多了后,套路基本上都差不多,关键把要保存的状态给抽象出来,保存下来. 简介: 顾名思义,所谓的数位DP就是按照数字的个,十,百,千--位数进行的DP.数位DP的题目有着非常明显的性质: 询问[l,r]的区间内,有多少的数字满足某个性质 做法根据前缀和的思想,求出[0,l-1]和[0,r]中满足性质的数的个数,然后相减即可. 算法核心: 关于数位DP,貌似写法还是

HDU 5917 Instability ramsey定理

http://acm.hdu.edu.cn/showproblem.php?pid=5917 即世界上任意6个人中,总有3个人相互认识,或互相皆不认识. 所以子集 >= 6的一定是合法的. 然后总的子集数目是2^n,减去不合法的,暴力枚举即可. 选了1个肯定不合法,2个也是,3个的话C(n, 3)枚举判断,C(n, 4), C(n, 5) #include <bits/stdc++.h> #define IOS ios::sync_with_stdio(false) using name

hdu 6166 Senior Pan

地址:http://acm.split.hdu.edu.cn/showproblem.php?pid=6166 题目: Senior Pan Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 245    Accepted Submission(s): 71 Problem Description Senior Pan fails i

2017中国大学生程序设计竞赛 - 网络选拔赛 HDU 6155 Subsequence Count 矩阵快速幂

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6155 题意: 题解来自:http://www.cnblogs.com/iRedBean/p/7398272.html 先考虑dp求01串的不同子序列的个数. dp[i][j]表示用前i个字符组成的以j为结尾的01串个数. 如果第i个字符为0,则dp[i][0] = dp[i-1][1] + dp[i-1][0] + 1,dp[i][1] = dp[i-1][1] 如果第i个字符为1,则dp[i][1