@description@

圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]。

相邻两份食物之间坐着一个人,共有 n 个人。每个人有两种选择,吃自己左边或者右边的食物。如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量。

假如某个人改变自己的选择后(其他 n-1 个人的选择不变),可以使自己获得比原先更多的热量,那么这个人会不满意。

请你给每个人指定应该吃哪一份食物,使得所有人都能够满意。

input

第一行一个整数 n (2<=n<=1000000),表示食物的数量(即人数)。食物和人都从1~n编号。

第二行包含 n 个整数 c[1], c[2], …, c[n](1<=c[i]<=10^9)。

假设第 i 个人(1<=i<n)左边是第 i 份食物,右边是第 i+1 份食物;而第 n 个人左边是第 n 份食物,右边是第 1 份食物。

output

如果不存在这样的方案,仅输出一行 NIE。

如果存在这样的方案,输出一行共 n 个整数,第 i 个整数表示第 i 个人选择的食物的编号。如果有多组这样的方案,输出任意一个即可。

sample input

5

5 3 7 2 9

sample output

2 3 3 5 1

@solution@

@version - 1@

网上的题解(至少在我能看到的)大多采用的是如下的方法:

我们记 dp[i][s] 表示第 i 份食物被左右两个人选择的状态为 s 是否合法(0 <= s < 4)

拆环成链:在第 n 个人的右边,即第 n + 1 份食物处放上第 1 份食物。

枚举第一份食物被吃的状态,递推到第 n + 1 份,再 check 第 n + 1 份的状态是否等于第一份食物。

dp 的时候记录转移,就可以输出方案了。

@version - 2@

然而。。。在我的不懈努力下,我乱搞出来了另外一个解法:

首先可以发现如果一个人某一边的食物 >= 另一边的两倍,则他选择这一边的食物一定不亏。

我们称这个人具有绝对占优策略。

这并不能说明什么。但是,考虑这样一个情况:所有人都不具有绝对占优策略。

这个时候,第 i 个人选择第 i 份食物总是一个合法的解。

证明可以采用反证法:如果第 i 个人改变选择而第 i + 1 个人不改变选择导致第 i 个人收益更大,则说明 c[i + 1] > c[i]*2。矛盾。

是否我们可以去掉具有绝对占优策略的人,再将剩下的人按上面的方法(第 i 个人选择第 i 份食物)进行处理?

对,也不完全对。

因为如果一个人的策略确定了,可能会导致其他人从不确定的状态转为确定的状态。

(举个例子:某个人旁边是 6 7,是不确定的。这个时候他旁边的人选择了 6,他变成了 3 7,就是确定的了)

需要在第一次去掉过后再继续寻找新的。

如果直接迭代 “寻找具有绝对占优策略的人”->“删除他们”,时间复杂度是 O(n^2) 的。

但是,我们发现一个人的策略只会影响他左右的人的策略。

所以可以将他左右的人加入队列(为了偷懒我用的栈),再进行进一步的处理。

这样就是 O(n) 的了。

同时这也表示不会存在无解的情况。

@accepted code@

给出两份代码,分别对应上面的两类解法。

@version - 1@

  1. #include<cstdio>
  2. const int MAXN = 1000000 + 5;
  3. inline int read() {
  4. int x = 0; char ch = getchar();
  5. while( ch > '9' || ch < '0' ) ch = getchar();
  6. while( '0' <= ch && ch <= '9' ) x = 10*x + ch-'0', ch = getchar();
  7. return x;
  8. }
  9. inline void write(int x) {
  10. if( !x ) return ;
  11. write(x/10);
  12. putchar(x%10 + '0');
  13. }
  14. int c[MAXN], pre[4][MAXN], n;
  15. bool dp[4][MAXN];
  16. void print(int i, int j) {
  17. if( j == 0 ) return ;
  18. print(pre[i][j], j - 1);
  19. printf("%d ", (j + (i&1)) > n ? 1 : (j + (i&1)) );
  20. }
  21. bool check(int x) {
  22. for(int i=0;i<=n;i++)
  23. dp[0][i] = dp[1][i] = dp[2][i] = dp[3][i] = false;
  24. dp[x][0] = true;
  25. for(int i=1;i<=n;i++) {
  26. if( dp[2][i-1] && c[i] <= c[i-1] ) dp[0][i] = true, pre[0][i] = 2;
  27. if( dp[3][i-1] && 2*c[i] <= c[i-1] ) dp[0][i] = true, pre[0][i] = 3;
  28. if( dp[0][i-1] && c[i] >= c[i-1] ) dp[1][i] = true, pre[1][i] = 0;
  29. if( dp[1][i-1] && 2*c[i] >= c[i-1] ) dp[1][i] = true, pre[1][i] = 1;
  30. if( dp[2][i-1] && c[i] <= 2*c[i-1] ) dp[2][i] = true, pre[2][i] = 2;
  31. if( dp[3][i-1] && 2*c[i] <= 2*c[i-1] ) dp[2][i] = true, pre[2][i] = 3;
  32. if( dp[0][i-1] && c[i] >= 2*c[i-1] ) dp[3][i] = true, pre[3][i] = 0;
  33. if( dp[1][i-1] && 2*c[i] >= 2*c[i-1] ) dp[3][i] = true, pre[3][i] = 1;
  34. }
  35. if( dp[x][n] ) {
  36. print(x, n);
  37. return true;
  38. }
  39. return false;
  40. }
  41. int main() {
  42. n = read();
  43. for(int i=0;i<n;i++)
  44. c[i] = read();
  45. c[n] = c[0];
  46. for(int i=0;i<4;i++)
  47. if( check(i) ) return 0;
  48. puts("NIE");
  49. }

@version - 2@

  1. #include<cstdio>
  2. const int MAXN = 1000000;
  3. int ans[MAXN + 5], tag[MAXN + 5], n;
  4. int c[MAXN + 5], stk[MAXN + 5], top = 0;
  5. inline int nxt(int x) {return (x + 1 == n) ? 0 : x + 1;}
  6. inline int pre(int x) {return (x == 0) ? n - 1 : x - 1;}
  7. inline bool Check(int i, int j) {
  8. if( tag[i] == -1 ) {
  9. if( tag[j] == 1 ) return c[i]*2 > c[j];
  10. else return c[i] > c[j];
  11. }
  12. else {
  13. if( tag[j] == 1 ) return c[i] > c[j];
  14. else return c[i] > c[j]*2;
  15. }
  16. }
  17. inline int read() {
  18. int x = 0; char ch = getchar();
  19. while( ch > '9' || ch < '0' ) ch = getchar();
  20. while( '0' <= ch && ch <= '9' ) x = 10*x + ch-'0', ch = getchar();
  21. return x;
  22. }
  23. void write(int x) {
  24. if( !x ) return ;
  25. write(x/10);
  26. putchar(x%10 + '0');
  27. }
  28. int main() {
  29. scanf("%d", &n);
  30. for(int i=0;i<n;i++) {
  31. c[i] = read();
  32. ans[i] = -1; tag[i] = 0;
  33. }
  34. for(int i=0;i<n;i++) {
  35. if( ans[i] != -1 ) continue;
  36. stk[++top] = i;
  37. while( top ) {
  38. int x = stk[top--];
  39. if( Check(x, nxt(x)) ) {
  40. ans[x] = x;
  41. if( ans[nxt(x)] == -1 ) stk[++top] = nxt(x), tag[nxt(x)] = -1;
  42. if( ans[pre(x)] == -1 ) stk[++top] = pre(x), tag[x] = 1;
  43. }
  44. else if( Check(nxt(x), x) ) {
  45. ans[x] = nxt(x);
  46. if( ans[nxt(x)] == -1 ) stk[++top] = nxt(x), tag[nxt(x)] = 1;
  47. if( ans[pre(x)] == -1 ) stk[++top] = pre(x), tag[x] = -1;
  48. }
  49. }
  50. }
  51. for(int i=0;i<n;i++)
  52. if( ans[i] == -1 ) ans[i] = i;
  53. for(int i=0;i<n;i++) write(ans[i] + 1), putchar(' ');
  54. puts("");
  55. }

@details@

一查,这道题居然还有什么专业背景,叫什么帕累托最优什么的……

不过这不重要。

dp 竟然卡我内存……不开 bool 还会 MLE……

如果我的乱搞做法有什么问题(因为我也没找到跟我一样做法的人所以不知道其正确性),请务必告诉我,谢谢。

@bzoj - 3749@ [POI2015] Łasuchy的更多相关文章

  1. BZOJ 3749: [POI2015]Łasuchy【动态规划】

    Description 圆桌上摆放着n份食物,围成一圈,第i份食物所含热量为c[i]. 相邻两份食物之间坐着一个人,共有n个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物, ...

  2. BZOJ 3749: [POI2015]Łasuchy(贪心)

    Orz大佬博客 CODE #include <bits/stdc++.h> using namespace std; typedef long long LL; char cb[1< ...

  3. [POI2015]Łasuchy

    [POI2015]Łasuchy 题目大意: 圆桌上摆放着\(n(n\le10^6)\)份食物,围成一圈,第\(i\)份食物所含热量为\(c_i\). 相邻两份食物之间坐着一个人,共有\(n\)个人. ...

  4. bzoj 4386: [POI2015]Wycieczki

    bzoj 4386: [POI2015]Wycieczki 这题什么素质,爆long long就算了,连int128都爆……最后还是用long double卡过的……而且可能是我本身自带大常数吧,T了 ...

  5. BZOJ 4385: [POI2015]Wilcze doły

    4385: [POI2015]Wilcze doły Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 648  Solved: 263[Submit][ ...

  6. BZOJ 4384: [POI2015]Trzy wieże

    4384: [POI2015]Trzy wieże Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 217  Solved: 61[Submit][St ...

  7. Bzoj 3747: [POI2015]Kinoman 线段树

    3747: [POI2015]Kinoman Time Limit: 60 Sec  Memory Limit: 128 MBSubmit: 553  Solved: 222[Submit][Stat ...

  8. BZOJ 3747 POI2015 Kinoman 段树

    标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...

  9. BZOJ 4380 [POI2015]Myjnie | DP

    链接 BZOJ 4380 题面 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个 ...

随机推荐

  1. Java review-basic3

    Mutexes, ReadWriteLock, ArrayBlockingQueue, Thread pools, LinkedList vs ArrayList, Object Pooling, R ...

  2. Django 使用模板页面,块标签,模型

    1.Django 使用模板页面 Django对于成体系的页面提出了模板继承和模板加载的方式. 1.导入静态页面 2.导入静态文件(css,js,images) 3.修改页面当中的静态地址 1.sett ...

  3. jsp页面el表达式<c:choose> <c:when的用法

    等于 是if else <c:choose> <c:when test="${paginationModel.py_province != ''}"> 如果 ...

  4. linux压缩打包

    linux下的压缩命令有tar.gzip.gunzip.bzip2.bunzip2. compress.uncompress.zip.unzip.rar.unrar等等,压缩后的扩展名有.tar..g ...

  5. BZOJ2069: [POI2004]ZAW

    2069: [POI2004]ZAW Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 303  Solved: 138[Submit][Status][D ...

  6. js实现翻转一个字符串

    字符串作在程序中是非常常见的,因为程序中绝大部分的数据都可以当作字符串来处理.在这里介绍几种翻转字符串的方法. (1)使用字符串函数 //使用数组翻转函数 function reverseString ...

  7. CF1067E Random Forest Rank

    CF1067E Random Forest Rank 可以证明: 一个树的邻接矩阵的秩,等于最大匹配数*2(虽然我只能证明下界是最大匹配) 而树的最大匹配可以贪心, 不妨用DP模拟这个过程 f[x][ ...

  8. Apache httpd问题汇总

    1.Apache服务器下载到本地解压到路径下:C:\ProgramFiles\Apache 后,启动时报错:serverroot must be a valid directory 解决办法:1.打开 ...

  9. python实现贝叶斯网络的概率推导(Probabilistic Inference)

    写在前面 这是HIT2019人工智能实验三,由于时间紧张,代码没有进行任何优化,实验算法仅供参考. 实验要求 实现贝叶斯网络的概率推导(Probabilistic Inference) 具体实验指导书 ...

  10. Primitive Topology

    原文:Primitive Topology 版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/u010333737/article/details/78 ...