砝码称重
代码
#include<iostream>
using namespace std;
//总重量的数量级是1e5,为了保险起见开了1e6的
const int N = 105,M=1e6+10;
bool f[N][M];//这里储存每个的状态,第一个表示砝码的数量,第二个表示重量
int a[N];//每个砝码的重量
int n,sum;
int main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];//计算砝码重量之和
}
f[0][0]=true;//当输入0个重量为0
for(int i=1;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
//分别表示了三种可能
//1.当前不动砝码
//2.重量加砝码,表示j+a[i]
//3.重量减去当前砝码,表示为j-a[i]
//当有一个是可行的那么就是可行的
f[i][j]=f[i-1][j]||f[i-1][j+a[i]]||f[i-1][abs(j-a[i])];
}
}
int ans=0;
for(int j=1;j<=sum;j++)
{if(f[n][j])ans++;}
//最后在n的情况下,有哪些重量是可得到的
cout<<ans<<endl;
}
疑问:
按照这个思想在录入时,将单个砝码的重量都表示为可以
for(int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
sum+=a[i];//计算砝码重量之和
f[1][a[i]]=true;
}
然后在下面循环中,放置两个砝码开始
for(int i=2;i<=n;i++)
{
for(int j=0;j<=sum;j++)
{
f[i][j]=f[i-1][j]||f[i-1][j+a[i]]||f[i-1][abs(j-a[i])];
}
}
按逻辑来想,应该是对的,但是连样例也过不了
总结:
知识点:dp,递推
这里类似01背包中,但是砝码的放置存在放左边和放右边的不同,所以比01背包多一个选项。
在做此类问题时,还是跟着y总的dp大法
1.状态表示
2.状态计算(状态的转移)
杨辉三角
代码
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
int n;
//求组合数
ll c(int a,int b)
{
ll res=1;
for(int i=a,j=1;j<=b;j++,i--)
{
res=res*i/j;
if(res>n)return res;
}
return res;
}
//二分查找里面最大的
bool check(int k)
{
int l=2*k,r=max(l,n);
while(l<r)
{
int mid=l+r>>1;//找出l与r一半的数
if(c(mid,k)>=n)
{
r=mid;
}
else{
l=mid+1;
}
}
if(c(r,k)!=n)return false;
//1ll是为了防止爆出int的数据范围
cout<<1ll*(r+1)*r/2+k+1<<endl;
return true;
}
int main()
{
cin>>n;
for(int k=16;;k--)
if(check(k))break;
return 0;
}
总结
知识点:二分,组合数
二分代码
这里的重点是如何取设置,r与l 然后怎么讲l与r跟mid 迭代
bool check(int k)
{
//这里需要理清怎么找边界
int l=2*k,r=max(l,n);
while(l<r)
{
int mid=l+r>>1;//找出l与r一半的数
if(c(mid,k)>=n)
{
r=mid;
}
else{
//需要理清这里是否要加1,否则会出现边界问题
l=mid+1;
}
}
if(c(r,k)!=n)return false;
//1ll是为了防止爆出int的数据范围
cout<<1ll*(r+1)*r/2+k+1<<endl;
return true;
}
组合数
//这里也用到双指针
ll c(int a,int b)
{
ll res=1;
for(int i=a,j=1;j<=b;j++,i--)
{
res=res*i/j;
if(res>n)return res;//当res比n大时无意义,直接返回
}
return res;
}