[A-Array_"蔚来杯"2022牛客暑期多校训练营6 (nowcoder.com)](ac.nowcoder.com/acm/contest… "A-Array_"蔚来杯"2022牛客暑期多校训练营6 (nowcoder.com)")
证明啥的没看懂,但这个把每个数都变成小于该数的最大的二次幂的方法是可以保证连续a[i]次至少有一次i的,找到最大的二次幂之后就开始找位置,如果这个位置是空的话就填入第i个数,然后这个位置往后k*d[i](d[i]是变成二次幂的a[i])都要变成i,这样就可保证每a[i]个位置都会有一个i了,并且d[i]<=a[i]所以这种做法是一定正确的
2022牛客多校6_A题_牛客博客 (nowcoder.net)
const int N = 2e5+5;
ll n;
ll redi(ll x){
ll res=1;
for(;res<=x;res<<=1);
res>>=1;
return res;
}
struct node{
ll id,val;
bool operator<(const node &a)const{
return val<a.val;
}
}a[200005];
ll ans[200005];
set<ll>st;
int main(){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&a[i].val);
a[i].val=redi(a[i].val);
a[i].id=i;
}
sort(a+1,a+n+1);
ll m=a[n].val;
for(int i=0;i<m;i++) st.insert(i);
for(int i=1;i<=n;i++){
ll s=*st.begin();
for(int j=s;j<m;j+=a[i].val){
if(st.count(j)){
st.erase(j);
ans[j]=a[i].id;
}
}
}
for(auto i:st) ans[i]=1;
printf("%lld\n",m);
for(int i=0;i<m;i++) printf("%lld ",ans[i]);
system("pause");
return 0;
}
p1443吃奶酪 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)状压dp
需要处理出每一对点的距离,然后把这些点的访问顺序看成是一个排列,然后dp[s][i]表示s的二进制为1的表示已经走到了,最右边是第i个点所花费的最小的距离,然后状态转移就是如果s的二进制位中如果该位是0的话选择要么加上这位要么不加
ll n;
double g[20][20],dp[100005][20],x[20],y[20];
void floyd(){
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(g[i][k]!=1e18)
for(int j=1;j<=n;j++)
g[i][j]=min(g[i][j],g[i][k]+g[k][j]);
}
double dis(ll i,ll j){
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main(){
scanf("%lld",&n);
x[1]=0;y[1]=0;n++;
for(int i=2;i<=n;i++) scanf("%lf%lf",&x[i],&y[i]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++) g[i][j]=1e18;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
g[i][j]=g[j][i]=dis(i,j);
floyd();
for(int s=0;s<(1<<n);s++)
for(int i=1;i<=n;i++)
dp[s][i]=1e18;
dp[0][0]=dp[1][1]=0;
for(int s=0;s<(1<<n);s++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j||(s>>(j-1))&1) continue;
dp[s|(1<<j-1)][j]=min(dp[s|(1<<j-1)][j],dp[s][i]+g[i][j]);
}
double ans=1e18;
for(int i=1;i<=n;i++) ans=min(ans,dp[(1<<n)-1][i]);
printf("%.2f\n",ans);
system("pause");
return 0;
}
P5911 [POI2004]PRZ - 洛谷 状压dp
这题是集合划分的一种类型,也可以说是分组,我们要做的是将n个人划分到若干个组里最后过桥时间总和最小,那我们可以枚举s和s的子集i,如果s的最后一个人i中也有的话就可以划分出i来,这是在假设s的最后一个人第一个被分出去的前提下进行的,这个操作用lowbit就可以,另外需要判断这个子集i是否合法,我们可以预处理出来,最后的答案一定是dp[s],表示所有人都过桥的最小时间,转移的时候也很好理解,dp[s]=min(dp[s],dp[s-i]+ti[i]),ti表示i所花费的时间,要么分出i要么不分i
const int N = 2e5+5;
ll W,n,t[20],w[20],dp[100005];
ll vis[100005],ti[100005];
int main(){
scanf("%lld%lld",&W,&n);
for(int i=1;i<=n;i++) scanf("%lld%lld",&t[i],&w[i]);
for(int s=0;s<(1<<n);s++){
ll sum=0;
ti[s]=0;
for(int i=1;i<=n;i++)
if(s>>(i-1)&1) sum+=w[i],ti[s]=max(ti[s],t[i]);
if(sum<=W) vis[s]=1;
// cout<<vis[s]<<" "<<s<<" "<<sum<<" "<<ti[s]<<endl;
}
for(int s=0;s<(1<<n);s++) dp[s]=1e18;
dp[0]=0;
for(int s=0;s<(1<<n);s++){
for(int i=s;i;i=(i-1)&s){
if(i&lowbit(s)&&vis[i])
dp[s]=min(dp[s],dp[s-i]+ti[i]);
}
}
printf("%lld\n",dp[(1<<n)-1]);
system("pause");
return 0;
}