@bzoj - 3749@ [POI2015] Łasuchy
@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的更多相关文章
- BZOJ 3749: [POI2015]Łasuchy【动态规划】
Description 圆桌上摆放着n份食物,围成一圈,第i份食物所含热量为c[i]. 相邻两份食物之间坐着一个人,共有n个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物, ...
- BZOJ 3749: [POI2015]Łasuchy(贪心)
Orz大佬博客 CODE #include <bits/stdc++.h> using namespace std; typedef long long LL; char cb[1< ...
- [POI2015]Łasuchy
[POI2015]Łasuchy 题目大意: 圆桌上摆放着\(n(n\le10^6)\)份食物,围成一圈,第\(i\)份食物所含热量为\(c_i\). 相邻两份食物之间坐着一个人,共有\(n\)个人. ...
- bzoj 4386: [POI2015]Wycieczki
bzoj 4386: [POI2015]Wycieczki 这题什么素质,爆long long就算了,连int128都爆……最后还是用long double卡过的……而且可能是我本身自带大常数吧,T了 ...
- BZOJ 4385: [POI2015]Wilcze doły
4385: [POI2015]Wilcze doły Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 648 Solved: 263[Submit][ ...
- BZOJ 4384: [POI2015]Trzy wieże
4384: [POI2015]Trzy wieże Time Limit: 20 Sec Memory Limit: 128 MBSubmit: 217 Solved: 61[Submit][St ...
- Bzoj 3747: [POI2015]Kinoman 线段树
3747: [POI2015]Kinoman Time Limit: 60 Sec Memory Limit: 128 MBSubmit: 553 Solved: 222[Submit][Stat ...
- BZOJ 3747 POI2015 Kinoman 段树
标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...
- BZOJ 4380 [POI2015]Myjnie | DP
链接 BZOJ 4380 题面 有n家洗车店从左往右排成一排,每家店都有一个正整数价格p[i]. 有m个人要来消费,第i个人会驶过第a[i]个开始一直到第b[i]个洗车店,且会选择这些店中最便宜的一个 ...
随机推荐
- 关系数据库理论 ch.6
6.1 问题的提出 关系模式是一个5元组 R U,D,DOM,F U 属性 D 域 DOM 属性到域的映射 F 依赖 在本章中将关系模式看作 三元组 R U,D 属性-依赖 1NF 每一个分量是不可分 ...
- 洛谷P2468 [SDOI2010]粟粟的书架
来了来了,随便拽一道题写题解[大雾] 最近发现自己基础奇差于是开始复习之前学过的东西,正好主席树我几乎完全没学会,然后打开洛谷试炼场… 发现了这么一道二合一的题. 这道题其实分成两个部分,前50%是一 ...
- 【风马一族_win10设置热点】win10无法开启热点怎么办
输入cmd,设置管理员开启 输入netsh wlan set hostednetwork mode=allow ssid=name(无线名字) key=00000000(8位以上密码); 输入nets ...
- NoSQL最新现状和趋势:云NoSQL数据库将成重要增长引擎
NoSQL最早起源于1998年,但从2009年开始,NoSQL真正开始逐渐兴起和发展.回望历史应该说NoSQL数据库的兴起,完全是十年来伴随互联网技术,大数据数据的兴起和发展,NoSQL在面临大数据场 ...
- LINUX下C++编程如何获得某进程的ID
#include <stdio.h> #include <stdlib.h> #include <unistd.h> using namespace std; pi ...
- 微信小程序--轮播图,标题,盒子,tab栏的合成例子
小程序是什么? 微信小程序,是一种不需要下载安装即可使用的应用,用户扫一扫或搜一下即可打开应用,在微信-发现-小程序可打开应用. 一.小程序的样式编写: 目录结构: app.json { " ...
- 三分钟学会在ASP.NET Core MVC 中使用Cookie
一.Cookie是什么? 我的朋友问我cookie是什么,用来干什么的,可是我居然无法清楚明白简短地向其阐述cookie,这不禁让我陷入了沉思:为什么我无法解释清楚,我对学习的方法产生了怀疑!所以我们 ...
- 软工作业——Alpha版本第一周小结
姓名 学号 周前计划安排 每周实际工作记录 自我打分 zxl 061425 1.进行任务分析2.进行任务分配 1.对任务进行了初步的划分,但还为进行给模块间的联系2.给每人分配了任务3.负责扫码签到功 ...
- Python3.7.4入门-5输入输出
5 输入输出 5.1 格式化字符串字面值 在字符串的开始引号或三引号之前加上一个 f 或 F .在此字符串中,你可以在 { 和 } 字符之间写可以引用的变量或字面值的 Python 表达式. > ...
- 7 种 Javascript 常用设计模式学习笔记
7 种 Javascript 常用设计模式学习笔记 由于 JS 或者前端的场景限制,并不是 23 种设计模式都常用. 有的是没有使用场景,有的模式使用场景非常少,所以只是列举 7 个常见的模式 本文的 ...