CF295C Greg and Friends
1.令船最多的载重(即题目中的 k)设为 m,50 千克的人的个数为 x,100 千克的人的个数为 y。考虑最劣情况,发现就是一个人把另一个拉过去后又自己回来然后又拉下一个人,以此类推,因此最多又 2n 个往返次数,即 4n 个过程(指船从一边到另一边的过程,后文均延用此定义),所以当过程数大于 4n 时,就说明无解。
2.设 f(i,j,k) 表示现在是第 i 个过程,这个过程结束后,还有 j 个 50 千克,k 个 100 千克的人没有过河的方案数,很明显一开始没有人渡河,所以 f(0,x,y)=1
(1)如果 i 是奇数,说明现在是到达对岸的过程,我们枚举这一次渡河的 50 千克的人数 a 与 100 千克的人数 b,则 f(i,j,k) 将要增加的贡献为 f(i-1,j+a,k+b)×C(j+a,a)×C(k+b,b)。
(2)如果 i 是偶数,说明现在是从对岸回来的过程,我们再次枚举这一次渡河的 50 千克的人数 a 与 100 千克的人数 b,则 f(i,j,k) 将要增加的贡献为 f(i-1,j-a,k-b)×C(x−j+a,a)×C(y−k+b,b)。
3.最后,每当进行 1 步骤的转移时,我们判断 f(i,0,0) 是否不为 0,如果不为 0,则输出答案,最后如果 i>4n 说明无解。
代码如下:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=60,mod=1e9+7;
int f[4*N][N][N],C[N][N];
int x,y,n,m;
inline int read(){
char c=getchar();
int f=1,ans=0;
while(c<48||c>57) f=(c==45?f=-1:1),c=getchar();
while(c>=48&&c<=57) ans=(ans<<1)+(ans<<3)+(c^48),c=getchar();
return ans*f;
}
signed main(){
n=read(),m=read();
for (int i=1;i<=n;i++){
int xx=read();
if (xx==50) x++;
else y++;
}
C[0][0]=1;
for (int i=1;i<=n;i++){
C[i][0]=1;
for (int j=1;j<=n;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
f[0][x][y]=1;
for (int i=1;i<=4*n;i++)
for (int j=0;j<=x;j++)
for (int k=0;k<=min(y,n-j);k++){
if (i&1){
for (int a=0;a<=x-j;a++)
for (int b=0;b<=y-k;b++)
if (!(a==0&&b==0)&&50*a+100*b<=m)
f[i][j][k]=(f[i][j][k]+f[i-1][j+a][k+b]*C[j+a][a]%mod*C[k+b][b]%mod)%mod;
if (f[i][0][0]){
cout <<i<<endl<<f[i][0][0];
return 0;
}
}
else{
for (int a=0;a<=j;a++)
for (int b=0;b<=k;b++)
if (!(a==0&&b==0)&&50*a+100*b<=m)
f[i][j][k]=(f[i][j][k]+f[i-1][j-a][k-b]*C[x-j+a][a]%mod*C[y-k+b][b]%mod)%mod;
}
}
puts("-1\n0");
return 0;
}
P5424 snakes G
1.设f[i][j]表示前ii条蛇,改变j次大小所得到的最小剩余空间
g[i][j]表示区间[i,j]的空余空间
那么可以得到如下转移方程:f[i][j]=min(f[i][j],f[k][j−1]+g[k+1][i])
k为枚举的一个断点,表示[k+1,i]这段区间用同一大小的网。
2.根据定义,一段区间的空余空间一定是用区间中的最大值作为网的大小,减去其他数字得到的和。即g[i][j]=maxn∗(j−i+1)−sum(i,j),其中区间和可以用前缀和O(1)处理。由于我们枚举断点的时候会从i−1i−1向下逐一枚举,那么我们可以在枚举断点的时候同时更新最大值。
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int MAX=410;
int n,m;
int a[MAX],f[MAX][MAX],s[MAX];
int main(){
cin>>n>>m;
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
}
m++;
memset(f,0x3f,sizeof(f));
f[0][0]=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=min(m,i);j++){
int mx=a[i];
for(int k=i-1;k>=0;k--){
f[i][j]=min(f[i][j],f[k][j-1]+mx*(i-k)-(s[i]-s[k]));
mx=max(mx,a[k]);
}
}
}
int ans=0x3f3f3f3f;
for(int i=0;i<=m;i++){
ans=min(ans,f[n][i]);
}
cout<<ans<<endl;
return 0;
}