bzoj 2298: [HAOI2011]problem a

来源:互联网 发布:宁波网络机柜回收 编辑:程序博客网 时间:2024/04/30 11:12

题目地址:http://www.lydsy.com/JudgeOnline/problem.php?id=2298


一道需要很奇葩的优化的动态规划


设dp[i]表示在前i名中最多有多少人说真话,则n-dp[n]即为所求。注意,如果有一个人的名次区间为[l, r],那么我们只在dp[r]中考虑他。

状态转移方程:dp[i] = max{dp[j-1]+sum[j][i]} 其中sum[j][i]表示名次区间为[j, i]的人数。

时间复杂度为O(n^2),需要进行优化。


考虑到大多数sum[j][i]为0,为冗余数据,不必枚举,所以我们建立一个数组match[i][j]来表示第j个数k使得sum[k][i]不为0(c++可以使用vector),减少决策的枚举量。

为了方便记录sum[i][match[i[j]],我使用了map(否则空间开不下,且申请内存时间过长),所以总时间复杂度为O(nlogn)


#include <iostream>#include <cstring>#include <cstdio>#include <string>#include <cstdlib>#include <vector>#include <map>#include <algorithm>#include <cmath>#define REP(i, n) for (int i = 0; i < (n); ++i)#define REP1(i, n) for (int i = 1; i <= (n); ++i)#define FOR(i, a, b) for (int i = (a); i <= (b); ++i)#define CLR(x, n) memset(x, n, sizeof(x))#define maxN 100010using namespace std;typedef pair<int, int> pii;void setIO(string name) {string in_f = name + ".in";string out_f = name + ".out";freopen(in_f.c_str(), "r", stdin);freopen(out_f.c_str(), "w", stdout);}int n;int dp[maxN];vector<int> match[maxN];map<pii, int> sum;void init() {scanf("%d", &n);int t1, t2, l, r;REP1(i, n) {scanf("%d%d", &t1, &t2);l = t1 + 1;r = n - t2;if (l > r) continue;if (sum.find(pii(l, r)) == sum.end()) {match[r].push_back(l);sum[pii(l, r)] = 1;}else {if (sum[pii(l, r)] < r - l + 1) ++sum[pii(l, r)];}}}void solve() {CLR(dp, 0);REP1(i, n) {dp[i] = dp[i - 1];int sz = match[i].size();REP(j, sz) {int t = match[i][j];dp[i] = max(dp[i], dp[t - 1] + sum[pii(t, i)]);}}printf("%d\n", n - dp[n]);}int main() {setIO("bzoj2298");init();solve();return 0;}


又是1A!虽然程序的效率排在倒数第二。。。(这份代码在本地测时,最长运行时间在500ms左右)


UPD: Martin提醒我说这道题应该用费用流做,我弱爆了!

原创粉丝点击