luogu P8497 [NOI2022] 移除石子
Posted 275307894a
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P8497 [NOI2022] 移除石子相关的知识,希望对你有一定的参考价值。
不好评价?
首先我们考虑最基础的情况,当 \\(k=0,l_i=r_i\\) 时,相当于我们需要判定一个状态能不能被消完。
这相当于我们要执行若干次 \\(2\\) 操作,使得每个位置要么大于等于 \\(2\\),要么为 \\(0\\)。为此我们需要挖掘一些操作 \\(2\\) 的性质。
性质 \\(1\\):操作区间长度不会超过 \\(5\\)
因为如果大于等于 \\(6\\) 就一定能拆成更小的区间。
性质 \\(2\\):对于每个点,以这个点为结尾的区间个数和以这个点为开头的区间个数都不会超过 \\(2\\),并且不会是长度为 \\(4\\) 与 \\(5\\) 的区间
假设有三个区间,则一定是 \\(3,4,5\\),那么将 \\(4,5\\) 的左端点用一次二操作,则 \\(4,5\\) 变成后一个点的 \\(3,4\\),这一定更优。
性质 \\(3\\):对于每个点,经过这个点的区间个数不会超过 \\(3\\)
我也不会证明……但是有了前两个结论写个暴力搜一搜应该是容易的吧!
于是我们大概就可以写出一个 dp 了。设 \\(dp_i,j,k\\) 表示到了第 \\(i\\) 个点,经过第 \\(i\\) 个点的区间个数为 \\(j\\),以第 \\(i\\) 个点为开头的区间个数为 \\(k\\),有没有一种可能。则这个应该满足 \\(k\\leq j,k\\leq 2,j\\leq 3\\) ,转移应该是平凡的,时间复杂度 \\(O(n)\\)。
再考虑到 \\(k> 0\\) 的情况,一种暴力的 dp 就是再加一维,复杂度变成 \\(O(nk^2)\\)。
虽然能过了,但是我们还是来尝试优化一下。
我们猜想,当 \\(a_i\\geq b\\) 的时候都是等价的,其中 \\(b\\) 是一个小常数。因为经过一个点的区间个数不超过 \\(3\\),而再把这个点消掉需要至少两个,因此 \\(b\\) 的一个下界是 \\(6\\),这已经很优秀了,但是实际上 \\(b\\) 是可以取到 \\(5\\) 的,不过差不多(
所以每个点放的个数不超过 \\(b\\),复杂度优化到 \\(O(nk)\\)。
另一个优化的角度是值和状态互换。如果放的石子数满足,当放 \\(k\\) 个石子可以 win时,放 \\(k+1\\) 个石子也可以满足,那么我们可以令 \\(dp_i,j,k\\) 为使用石子数的最小值,这样复杂度就是 \\(O(n)\\) 的。但是这满不满足呢?
当 \\(k=0\\) 的时候显然是满足的,当 \\(k=1\\) 的时候有两种情况不满足:全 \\(0\\),或者 \\(n=3\\) 且全 \\(1\\)。
当 \\(k\\geq 2\\) 的时候,只需要考虑 \\(k=1\\) 的两种不满足情况。全 \\(0\\) 显然不会出现,如果 \\(n=3\\) 且全 \\(1\\) 我们就可以放成两个 \\(2\\) 一个 \\(0\\)。因此对于 \\(k\\geq 2\\) 都是满足的。 \\(k=1\\) 的情况特判就行。
状态看上去不是很多,之后就是套路 dp 套 dp 了。如果按照第一种方法压状态在 \\(k=100\\) 的时候大概是 \\(5\\times 10^4\\) 个状态,光把这些东西搜出来就 T 了。按照第二种方法搜大概是 \\(1.2\\times 10^4\\) 个状态,时间复杂度 \\(O(TnS(n))\\),卡卡常就能过。
[luogu]P1800 software_NOI导刊2010提高(06)[DP][二分答案]
software_NOI导刊2010提高(06)
题目描述
一个软件开发公司同时要开发两个软件,并且要同时交付给用户,现在公司为了尽快完成这一任务,将每个软件划分成m个模块,由公司里的技术人员分工完成,每个技术人员完成同一软件的不同模块的所用的天数是相同的,并且是已知的,但完成不同软件的一个模块的时间是不同的,每个技术人员在同一时刻只能做一个模块,一个模块只能由一个人独立完成而不能由多人协同完成。一个技术人员在整个开发期内完成一个模块以后可以接着做任一软件的任一模块。写一个程序,求出公司最早能在什么时候交付软件。
输入输出格式
输入格式:
输入文件第一行包含两个由空格隔开的整数n和m,其中1<=n<=100,1<=m<=100,接下来的n行每行包含两个用空格隔开的整数d1和d2,d1表示该技术人员完成第一个软件中的一个模块所需的天数,d2表示该技术人员完成第二个软件中的一个模块所需的天数,其中1<= d1,d2<=100。
输出格式:
输出文件仅有一行包含一个整数d,表示公司最早能于d天后交付软件。
输入输出样例
输入样例1#:
3 20
1 1
2 4
1 6
输出样例1#:
18
说明
【样例】
最快的方案是第一个技术人员完成第二个软件的18个模块,用时18天,第三个技术人员完成第一个软件的18个模块,用时18天,其余的模块由第二个技术人员完成,用时12天,做完所有模块需要18天。如果第一个技术人员完成第二个软件的17个模块,第三个技术人员完成第一个软件的17个模块,其余的模块由第二个技术人员完成,需要用时18天,做完所有模块仍然需要18天,所以少于18天不可能做完所有模块。
一道神奇的题目,容易想到答案是单调的,考虑二分答案。
但是检验...一开始要贪心的,想想完全不行啊啊啊!!!
可恶,偷看一下神犇的题解,真的牛!
每个人其实相互独立,各干各的,最后求他们的最大值,最小化最大值。
所以我们只需要让n个人第一个做满,最大化第二个,如果>=m,则可以。
所以我们用f[i][j]表示前i个人第一个软件做了j个,可以最多做的第二个软件。
转移:
f[i][j]=Max{f[i-1][j-k]+(mid-d1[i]*k)/d2[i]} 【如果第i个选择了k个,只剩下(mid-d1[i])/d2[i]个可以做(将每个决策独立出来,好方法,NB。)】
还有就是可恶的边界,不存在的状态要初始化一个很小的数。f[0][0]=0;
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 inline int read(); 6 int Max(int x,int y){return x>y?x:y;} 7 int Min(int x,int y){return x<y?x:y;} 8 namespace lys{ 9 const int N = 1e2 + 7 ; 10 int dp[N][N],d1[N],d2[N]; 11 int n,m; 12 bool chk(int mid){ 13 int i,j,k; 14 memset(dp,-127,sizeof dp); 15 dp[0][0]=0; 16 for(i=1;i<=n;i++) 17 for(j=0;j<=m;j++) 18 for(k=0;k<=Min(j,mid/d1[i]);k++) dp[i][j]=Max(dp[i][j],dp[i-1][j-k]+(mid-d1[i]*k)/d2[i]); 19 return (dp[n][m]>=m); 20 } 21 int main(){ 22 int i; 23 n=read(); m=read(); 24 for(i=1;i<=n;i++) d1[i]=read(),d2[i]=read(); 25 int l=1,r=20000,mid; 26 while(l<r){ 27 mid=(l+r)>>1; 28 if(chk(mid)) r=mid; 29 else l=mid+1; 30 } 31 printf("%d\n",l); 32 return 0; 33 } 34 } 35 int main(){ 36 lys::main(); 37 return 0; 38 } 39 inline int read(){ 40 int kk=0,ff=1; 41 char c=getchar(); 42 while(c<‘0‘||c>‘9‘){ 43 if(c==‘-‘) ff=-1; 44 c=getchar(); 45 } 46 while(c>=‘0‘&&c<=‘9‘) kk=kk*10+c-‘0‘,c=getchar(); 47 return kk*ff; 48 }
以上是关于luogu P8497 [NOI2022] 移除石子的主要内容,如果未能解决你的问题,请参考以下文章
Luogu ????????????????????? ([NOI2014]?????????????????????)