https://vijos.org/p/1037
问你用以下的数字构建两个 相同高度的双塔,尽可能的高。
开始的思路是 背包计数。
如果 dp[m] 和dp[m/2] 和m%2==0 都满足,那么我们就可以输出m/2
(m和 m/2 这些数字都能拼出来。。)
但是即使这样,其实也不一定是可以的,因为很可能 m/2构成的两次使用了重叠的项,而我们并没有阻止这一种情况
// 这时错误的代码。过了不到一半的数据qwq
#include <bits/stdc++.h>
using namespace std;
const int maxn=3000;
int a[maxn];
int dp[maxn];
int m;
int main()
{ scanf("%d",&m);
int sum=0;
for(int i=0;i<m;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
memset(dp,0,sizeof(dp));
dp[0]=1;
for(int i=0;i<m;i++){
for(int j=sum;j>=a[i];j--)
dp[j]+=dp[j-a[i]];
}
int x=sum;
while(1){
if(dp[x]&&x%2==0&&dp[x/2]>=2){
printf("%d\n",x/2);
break;
}
else
x--;
}
return 0;
}
正解是 类似背包的dp
dp[i][j] 表示使用了1-i个数字,两个塔差值为j时候 矮塔的个数。
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=3000;
int a[maxn];
int dp[2][maxn];
int m;
int main()
{ scanf("%d",&m);
int sum=0;
for(int i=1;i<=m;i++){
scanf("%d",&a[i]);
sum+=a[i];
}
for(int i=0;i<2;i++){
for(int j=1;j<maxn;j++)
dp[i][j]=-inf;
}
for(int i=1;i<=m;i++){
for(int j=0;j<=sum;j++){
dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j]);//不使用 当前第i个数字
if(j>=a[i])
dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j-a[i]]);
// 将这个数字放在高塔上。(这点注意思考)
if(j+a[i]<=sum)
dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][j+a[i]]+a[i]);// 放在矮塔上。大小并未改变。
if(j<a[i])
dp[i%2][j]=max(dp[i%2][j],dp[(i-1)%2][a[i]-j]+a[i]-j);//放在矮塔上,埃塔变成了高塔。(这个数字大于差值。改变了
)
}
}
bool flag=false;
if(dp[(m)%2][0]){
printf("%d\n",dp[(m)%2][0]);
flag=true;
}
if(!flag)
puts("Impossible");
return 0;
}