第十五周总结

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;
}
}

3、硬币的面值 - 洛谷

题意:就是用所给的硬币凑出所有不超过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;
}

4、2385 -- Apple Catching

题意:牛吃苹果,然后他懒得运动,初始在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;
}

5、Problem - 2955

这题就比较好了,

题意:一个小偷去偷东西,然后让我们求,在不超过最大被抓概率下,得到的最大财富。

坑点:

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;
}

总结一下:后面时间越来越紧了,没太有时间学,学习效率太低,最多也就能干好一件事,前期投入大量精力去学这门课,学的虽然也比较少,但是比较充实,后面这段时间心浮气躁的,没太好好学,现在早晨很难起来,迟到了好几次,心确实也不安,也因此错过了好多,今天比赛也是听别人说的,进行到一半才开始,就做了三个简单题。这周学解决背包问题,题大体看了看,没了解的特别深,只能先浅学一下了,以后有机会再接触,再拾起来好好的再学学。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值