动态规划:高楼扔鸡蛋:
题目:
思考点:
dp min res=是对每一层都算一下,求最小次数
用字典做备忘录不错,但这里用数组也可,而且不用这种方法?剪枝?,会爆炸
但每一次[递归的每一层]又要取最大值,因为最坏情况
有两个错误:忘写了等于号,和记录防止重复计算的数组应写在循环外面
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct aa{
int k;
int n;
};
map<aa,int>dic;
int dic2[1000][1000]={0};
int dp(int k,int n){
if(k==1){
return n;
}if(n==0){
return 0;
}
// if(dic[{k,n}]){
// return dic[{k,n}];
// }
if(dic2[k][n]!=0){
return dic2[k][n];
}
int res=10000;
for(int i=1;i<=n;i++)
{
int dpsui,dpweisui;
///等价于下面的模块留下来因为出错了
dpsui=dp(k-1,i-1);
dpweisui=dp(k,n-i);
if(dpsui>=dpweisui){///要写等于号
res=min(res,dpsui+1);
// dic[{k,n}]=res;
}
if(dpsui<dpweisui){
res=min(res,dpweisui+1);
// dic[{k,n}]=res;
}
///等价于上面的模块
///res=min(res,max(dp(k,n-i),dp(k-1,i-1))+1);
}
dic2[k][n]=res; ///应该写在循环外面
return res;
}
int main()
{
int k=2,n=100;
cout<<dp(k,n);
return 0;
}
2020.9.10写出来的代码:
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int dp[105][105]={0};
int finder(int k,int n){/k:jidanshu n:loucengshu
if(dp[k][n]!=0){return dp[k][n];}
if(n==0)return 0;
if(k==1)return n;
int res=10000;
zuo:suile you:meisui
for(int i=1;i<=n;i++){
res=min(res,max(finder(k-1,i-1),finder(k,n-i))+1);
}
dp[k][n]=res;
return res;
}
int main()
{
cout<<finder(2,100);
return 0;
}
2020.11.13
#include <iostream>
#include<bits/stdc++.h>
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;
#define maxn 10005
int mem[maxn][maxn]={0};
int dp(int k,int n)
{
if(k==1)
{
return n;
}
if(n==0)
{
return 0;
}
if(mem[k][n]!=0)
{
return mem[k][n];
}
int res=maxn;
for(int i=1;i<=n;i++)
{
res=min(res,max(//这样也能过编译
dp(k-1,i-1),//sui//错误原因,这里之前没i-1
dp(k,n-i))+1//unsui
);
}
mem[k][n]=res;
return mem[k][n];
}
int main(int argc, char** argv) {
cout<<dp(2,100);
return 0;
}//out::14
高楼鸡蛋进阶
二分优化
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct aa{int k;int n;};
map<aa,int>dic;
int dic2[1000][1000]={0};
int dp(int k,int n){
if(k==1){return n;}if(n==0){return 0;}if(dic2[k][n]!=0){return dic2[k][n];}
int res=10000;
int lo,hi,mid;
lo=1;
hi=n;
///二分法查找
while(lo<=hi){
mid=(lo+hi)/2;
int broken=dp(k-1,mid-1);
int un_broken=dp(k,n-mid);
if(broken>un_broken){
hi=mid-1;
res= min(res,broken+1);///res仍需要不断更新(要最小)
continue;
}
if(broken<un_broken){
lo=mid+1;
res=min(res,un_broken+1);
continue;
}
if(broken==un_broken){///这里可认为恰好找到了,直接回,而且直接在上面随便一个加等号也可以
res=min(res,broken+1);
dic2[k][n]=res;
return res;
}
}
dic2[k][n]=res; ///应该写在循环外面
return res;
}
int main()
{
int k=2,n=100;
cout<<dp(k,n);
return 0;
}
2020.9.10重写了一个,上次的代码全有return,浪费了,又写出了个bug(应该是最坏情况(v2)),标好了
(v2里没用min,原代码用了,咋说,也没出问题,)
v1
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int dp[105][105]={0};
int finder(int k,int n){/k:jidanshu n:loucengshu
if(dp[k][n]!=0){return dp[k][n];}
if(n==0)return 0;
if(k==1)return n;
int res=10000;
int le,ri,mid,sui,wei_sui;
zuo:suile you:meisui
le=1;
ri=n;
while(le<=ri)
{
mid=(le+ri)/2;
sui=finder(k-1,mid-1);
wei_sui=finder(k,n-mid);
///zuididian
if(sui<wei_sui){///(zuo)
le=mid+1;
res=min(res,wei_sui+1);
continue;
}
if(sui>wei_sui)
{
ri=mid-1;
res=min(res,sui+1);
continue;
}
if(sui==wei_sui)
{
printf("yes");
res=min(res,sui+1);
break;
}
}
dp[k][n]=res;
return res;
}
int main()
{
cout<<finder(2,100);
return 0;
}
2020.9.10v2
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int dp[105][105]={0};
int finder(int k,int n){/k:jidanshu n:loucengshu
if(dp[k][n]!=0){return dp[k][n];}
if(n==0)return 0;
if(k==1)return n;
int res=10000;
int le,ri,mid,sui,wei_sui;
zuo:suile you:meisui
le=1;
ri=n;
while(le<=ri)
{
mid=(le+ri)/2;
sui=finder(k-1,mid-1);
wei_sui=finder(k,n-mid);
///zuididian
if(sui<wei_sui){///(zuo)
le=mid+1;
res=wei_sui+1;///这里要选大的那一个
continue;
}
if(sui>wei_sui)
{
ri=mid-1;
res=sui+1;///这里要选大的那一个
continue;
}
if(sui==wei_sui)
{
printf("yes");
res=sui+1;
break;
}
}
dp[k][n]=res;
return res;
}
int main()
{
cout<<finder(2,100);
return 0;
}
重定义状态转移(我感觉上次应该是忘写了)
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
struct aa{int k;int n;};
map<aa,int>dic;
int dic2[1000][1000]={0};
int dp(int k,int n){
if(k==1){return n;}if(n==0){return 0;}if(dic2[k][n]!=0){return dic2[k][n];}
int res=10000;
int lo,hi,mid;
lo=1;
hi=n;
///二分法查找
while(lo<=hi){
mid=(lo+hi)/2;
int broken=dp(k-1,mid-1);
int un_broken=dp(k,n-mid);
if(broken>un_broken){
hi=mid-1;
res= min(res,broken+1);///res仍需要不断更新(要最小)
continue;
}
if(broken<un_broken){
lo=mid+1;
res=min(res,un_broken+1);
continue;
}
if(broken==un_broken){///这里可认为恰好找到了,直接回,而且直接在上面随便一个加等号也可以
res=min(res,broken+1);
dic2[k][n]=res;
return res;
}
}
dic2[k][n]=res; ///应该写在循环外面
return res;
}
int main()
{
int k=2,n=100;
cout<<dp(k,n);
return 0;
}
2020.9.10更新·
#include <iostream>
#include<bits/stdc++.h>
using namespace std;
int dp[105][105]={0};
int finder(int k,int n){/k:jidanshu n:loucengshu
int m=0;
while(dp[k][m]<n){
m++;
for(int kk=1;kk<=k;kk++)
{
dp[kk][m]=dp[kk][m-1]+dp[kk-1][m-1]+1;
}
}
return m;
}
int main()
{
int k,n;
cin>>k>>n;
cout<<finder(k,n);
return 0;
}