@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@

#include<cstdio>
const int MAXN = 1000000 + 5;
inline int read() {
int x = 0; char ch = getchar();
while( ch > '9' || ch < '0' ) ch = getchar();
while( '0' <= ch && ch <= '9' ) x = 10*x + ch-'0', ch = getchar();
return x;
}
inline void write(int x) {
if( !x ) return ;
write(x/10);
putchar(x%10 + '0');
}
int c[MAXN], pre[4][MAXN], n;
bool dp[4][MAXN];
void print(int i, int j) {
if( j == 0 ) return ;
print(pre[i][j], j - 1);
printf("%d ", (j + (i&1)) > n ? 1 : (j + (i&1)) );
}
bool check(int x) {
for(int i=0;i<=n;i++)
dp[0][i] = dp[1][i] = dp[2][i] = dp[3][i] = false;
dp[x][0] = true;
for(int i=1;i<=n;i++) {
if( dp[2][i-1] && c[i] <= c[i-1] ) dp[0][i] = true, pre[0][i] = 2;
if( dp[3][i-1] && 2*c[i] <= c[i-1] ) dp[0][i] = true, pre[0][i] = 3;
if( dp[0][i-1] && c[i] >= c[i-1] ) dp[1][i] = true, pre[1][i] = 0;
if( dp[1][i-1] && 2*c[i] >= c[i-1] ) dp[1][i] = true, pre[1][i] = 1;
if( dp[2][i-1] && c[i] <= 2*c[i-1] ) dp[2][i] = true, pre[2][i] = 2;
if( dp[3][i-1] && 2*c[i] <= 2*c[i-1] ) dp[2][i] = true, pre[2][i] = 3;
if( dp[0][i-1] && c[i] >= 2*c[i-1] ) dp[3][i] = true, pre[3][i] = 0;
if( dp[1][i-1] && 2*c[i] >= 2*c[i-1] ) dp[3][i] = true, pre[3][i] = 1;
}
if( dp[x][n] ) {
print(x, n);
return true;
}
return false;
}
int main() {
n = read();
for(int i=0;i<n;i++)
c[i] = read();
c[n] = c[0];
for(int i=0;i<4;i++)
if( check(i) ) return 0;
puts("NIE");
}

@version - 2@

#include<cstdio>
const int MAXN = 1000000;
int ans[MAXN + 5], tag[MAXN + 5], n;
int c[MAXN + 5], stk[MAXN + 5], top = 0;
inline int nxt(int x) {return (x + 1 == n) ? 0 : x + 1;}
inline int pre(int x) {return (x == 0) ? n - 1 : x - 1;}
inline bool Check(int i, int j) {
if( tag[i] == -1 ) {
if( tag[j] == 1 ) return c[i]*2 > c[j];
else return c[i] > c[j];
}
else {
if( tag[j] == 1 ) return c[i] > c[j];
else return c[i] > c[j]*2;
}
}
inline int read() {
int x = 0; char ch = getchar();
while( ch > '9' || ch < '0' ) ch = getchar();
while( '0' <= ch && ch <= '9' ) x = 10*x + ch-'0', ch = getchar();
return x;
}
void write(int x) {
if( !x ) return ;
write(x/10);
putchar(x%10 + '0');
}
int main() {
scanf("%d", &n);
for(int i=0;i<n;i++) {
c[i] = read();
ans[i] = -1; tag[i] = 0;
}
for(int i=0;i<n;i++) {
if( ans[i] != -1 ) continue;
stk[++top] = i;
while( top ) {
int x = stk[top--];
if( Check(x, nxt(x)) ) {
ans[x] = x;
if( ans[nxt(x)] == -1 ) stk[++top] = nxt(x), tag[nxt(x)] = -1;
if( ans[pre(x)] == -1 ) stk[++top] = pre(x), tag[x] = 1;
}
else if( Check(nxt(x), x) ) {
ans[x] = nxt(x);
if( ans[nxt(x)] == -1 ) stk[++top] = nxt(x), tag[nxt(x)] = 1;
if( ans[pre(x)] == -1 ) stk[++top] = pre(x), tag[x] = -1;
}
}
}
for(int i=0;i<n;i++)
if( ans[i] == -1 ) ans[i] = i;
for(int i=0;i<n;i++) write(ans[i] + 1), putchar(' ');
puts("");
}

@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. 2019.9.28 csp-s模拟测试54 反思总结

    咕咕咕的冲动如此强烈x T1x: 看完题目想了想,感觉把gcd不为1的强行放在一组,看作一个连通块,最后考虑连通块之间的组合方式就可以了. 然后维护这个连通块可以写并查集可以连边跑dfs怎么着都行… ...

  2. POJ4852 Ants

    Ants Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 20047   Accepted: 8330 Description ...

  3. 解决git的the remote end hung up问题_百度经验

    使用git更新或提交中途有时出现The remote end hung up unexpectedly的异常,特别是资源库在国外的情况下.此问题可能由网络原因引起. 工具/原料   git 方法/步骤 ...

  4. 使用log4j打印日志

    在项目中我们必不可少需要打印日志,通过日志我们可以查看系统的运行状态是否正常,当程序出现异常的时候,我们也可以通过查看日志来定位问题的位置,给程序员的工作带来了极大的便利. 以下这边博客的内容是我从一 ...

  5. Spring 社区的首个国产开源项目顺利毕业

    相信大家对上周的 <来自 Spring Cloud 官方的消息,Spring Cloud Alibaba 即将毕业>文章记忆犹新.本周,Spring Cloud Alibaba 正式毕业, ...

  6. HTML input type=file文件选择表单的汇总(一)

    HTML input type=file 在onchange上传文件的过程中,遇到同一个文件二次上传无效的问题. 最近在做项目过程中,遇到同一文件上传的时候,二次上传没有效果,找了资料,找到了原因: ...

  7. asp.net技术(公共方法)

    #region 获取 本周.本月.本季度.本年 的开始时间或结束时间 /// <summary> /// 获取开始时间 /// </summary> /// <param ...

  8. Google搜索技巧-入门篇

    基本搜索 Google 查询简洁方便,仅需输入查询内容并敲一下回车键 (Enter),或单击“Google 搜索”按钮即可得到相关资料. 搜索两个及两个以上关键字 Google 只会返回那些符合您的全 ...

  9. 优化SQL之最佳索引

    SQL优化工具Tosska SQL Tuning Expert for Oracle,帮助SQL开发人员解决SQL性能问题. 本工具主要创始人Richard To, 资深ITPUB元老,从1996年开 ...

  10. 阿里云 EMAS HTTPDNS 联合函数计算重磅推出 SDNS 服务,三大能力获得突破

    1. 什么是 HTTPDNS ? 传统的 DNS(Domain Name System)使开发者常面临着域名劫持.调度不精准的问题. HTTPDNS 使用 HTTP 协议替换常用的 UDP 协议,完成 ...