1、
题意:给出n个物品装容量为M的背包,对于每一个物品i,问当i缺失的时候用剩下i-1个物品装满背包的方案数(1<n,m<=2e3)
首先考虑一个整的01背包,用傻瓜方法得出f[j]为背包体积为j的时候装满背包的方案书。为然后对于单个物品i,定义g[j]为i物品缺失的时候装满j体积的方案数,直接求得这个结果是有些复杂的(因为需要重新做一次背包,如此的时间复杂度自然是1s承受不了的),因此我们选择容斥,用f[j]减去必定选择此物品i的方案数,此状态可以直接由g[j-w[i]]转移过来,最后输出g[m]即可得出结果
#include<bits/stdc++.h>
using namespace std;
const int MOD=10;
const int MAXN=2e3+5;
int f[MAXN],g[MAXN],w[MAXN];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>w[i];
}
f[0]=1;
for(int i=1;i<=n;i++)
{
for(int j=m;j>=w[i];j--)
{
f[j]=(f[j]+f[j-w[i]])%10;
}
}
for(int i=1;i<=n;i++)
{
for(int j=0;j<=m;j++)
{
if(j<w[i])
{
g[j]=f[j];
continue;
}
else g[j]=(f[j]-g[j-w[i]]+10)%10;
}
for(int j=1;j<=m;j++)
{
cout<<g[j];
j==m?puts(""):
cout<<" ";
}
}
return 0;
}
2、
题意:给出背包容量和物品体积,问有多少种方案可以使得该背包再也无法装下任何一个物品
将物品顺序排列,则答案可以抽象为,针对每个物品,所有比该物品体积小的全部装进背包,统计用比该物品更大的物品来装满背包,使得该物品无法被装入的方案数,最终答案为这些方案数求和
假设选择的比该物品更大的物品和为sum1,小于该物品的和为sum2该物品体积为vi,背包大小为m,则有:
m-sum2-vi < sum1<=m-sum2
求得这个范围内所有sum1的和
顺序排列之后求后缀和,然后倒序背包求出前K大物品装满背包的方案数,最后把所需答案求和即可得出结果
#include<bits/stdc++.h>
#define LL long long
const int MAXM=1e3+7;
const int MAXN=55;
int v[MAXN],dp[MAXN][MAXM];
LL sum[MAXN];
bool cmp(const int &a,const int &b)
{
return a>b;
}
int main()
{
int T;
cin>>T;
for(int cas=1;cas<=T;cas++)
{
int ans=0,cnt=1;
memset(v,0,sizeof(v));
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
cin>>v[i];
}
sort(v+1,v+n+1,cmp);
if(v[n]>m)
{
cout<<endl<<cas;
continue ;
}
for(int i=n;i>=1;i--)
{
sum[i]=sum[i+1]+v[i];
}
for(int i=0;i<=n;i++)
{
dp[i][0]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=v[i];j<=m;j++)
{
dp[i][j]=dp[i-1][j]+dp[i-1][j-v[i]];
}
}
for(int i=n;i>=1;i--)
{
for(int j=max(m-sum[i+1]-v[i]+1,0ll);m>=sum[i+1]&&j<=m-sum[i+1];j++)
{
ans=ans+dp[i-1][j];
}
}
cout<<cas<<ans;
}
return 0;
}
}
题意:就是用所给的硬币凑出所有不超过m的数,因而数组中一定要有1的存在,没有1的话则不可能完成。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2000010;
const int INF = 0x3f3f3f3f;
long long n, m, a[maxn];
int main()
{
scanf("%lld%lld", &n, &m);
{
for(int i = 0; i < n; i++) scanf("%lld", &a[i]);
a[n] = m; // 把 m放入数组中,作为最大数,把数组中的数全部实现。
sort(a, a + n + 1); // 排序
if(a[0] != 1) {
printf("No answer!!!\n");
return 0;
}
long long cnt = 0, ans = 0;
for(int i = 0; i < n; i++)
{
while(cnt < a[i + 1] - 1)
{
cnt += a[i];
ans++;
if(cnt >= m) // 已经实现了m
{
printf("%lld\n", ans);
return 0;
}
}
}
printf("%lld\n", ans + 1);
}
return 0;
}
题意:牛吃苹果,然后他懒得运动,初始在1树下,只能移动k次,问最多拿到的苹果数。在这里我用了三维数组来记录牛的状态,其实也可以j来直接记录if(j%2 + 1 == a[i]) dp[i][j]++;,比较初始状态是确定的。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int t, w, a[maxn], dp[maxn][35][5];
int main()
{
while(cin>>t>>w)
{
for(int i = 1; i <= t; ++i) cin>>a[i];
for(int i = 1; i <= t; ++i)
{
for(int j = 0; j <= w; j++)
{
if(j == 0)
{
dp[i][j][1] = dp[i - 1][j][1];
dp[i][j][2] = dp[i - 1][j][2];
dp[i][j][a[i]]++;
}
else
{
dp[i][j][1] = max(dp[i - 1][j][1], dp[i - 1][j - 1][2]);
dp[i][j][2] = max(dp[i - 1][j][2], dp[i - 1][j - 1][1]);
dp[i][j][a[i]]++;
}
}
}
cout<<max(dp[t][w][1], dp[t][w][2]);
}
return 0;
}
这题就比较好了,
题意:一个小偷去偷东西,然后让我们求,在不超过最大被抓概率下,得到的最大财富。
坑点:
1.概率不是用来加的,抢完一个银行再去抢另一个银行时,概率需要成,一开始考虑的是概率*100,以他做背包的一维,但如果这样做的化最坏情况约为10^100,很遗憾多大的数组也无法满足我。
2.如果正向做的化,求被抓概率相当繁琐,不能确定哪次被抓,所以我们需要反向考虑最大不被抓的概率。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 110 * 110;
int t, n, m[maxn], sum;
double ht, p, h[maxn], dp[maxn];
int main()
{
cin>>t;
while(t--)
{
sum = 0;
cin>>p>>n;
memset(dp, 0, sizeof(dp));
for(int i = 0; i < n; ++i)
{
cin>>m[i]>>h[i];
sum += m[i];
}
dp[0] = 1.0;
for(int i = 0; i < n; ++i)
{
for(int j = sum; j >= m[i]; --j)
{
dp[j] = max(dp[j], dp[j - m[i]] *(1 - h[i]));
}
}
for(int i = sum; i >= 0; i--)
{
if((1- dp[i]) < p)
{
cout<<i<<endl;
break;
}
}
}
return 0;
}
总结一下:后面时间越来越紧了,没太有时间学,学习效率太低,最多也就能干好一件事,前期投入大量精力去学这门课,学的虽然也比较少,但是比较充实,后面这段时间心浮气躁的,没太好好学,现在早晨很难起来,迟到了好几次,心确实也不安,也因此错过了好多,今天比赛也是听别人说的,进行到一半才开始,就做了三个简单题。这周学解决背包问题,题大体看了看,没了解的特别深,只能先浅学一下了,以后有机会再接触,再拾起来好好的再学学。