计蒜客T2051完全背包+原理证明

题目链接

题目大致内容就是根据一个货币系统a[1,2,…n],找到一个等价的货币系统b[1,2,3,…m],使得m最小,求m。

对于这道题,聪明人一眼就看出来了解题思路(当然,我显然是不聪明的/(ㄒoㄒ)/~~)。

***具体思路是:***对于货币系统a中的任意一种货币,我们先从小到大排序,如果大的货币可以被所有比它小的货币的表示出来,那么这种货币就可以被去掉。
这样,我们就可以用完全背包来解题。背包容量为所有货币种类中的最大值,也就是排序之后的a[n]。

具体代码如下,我加上了注释,方便小伙伴们理解。

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<string.h>
#define pl printf("\n")
#define pk printf(" ")
#define ll long long int
using namespace std;
void in(int& x) {
    int f = 1;
    x = 0;
    char ch = getchar();

    while (ch < '0' || ch > '9')   {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
void inl(ll& x) {
    int f = 1;
    x = 0;
    char ch = getchar();

    while (ch < '0' || ch > '9')   {if (ch == '-') f = -1; ch = getchar();}
    while (ch >= '0' && ch <= '9') {x = x * 10 + ch - '0'; ch = getchar();}
    x *= f;
}
void out(int a) {
    if(a < 0) { putchar('-'); a = -a; }
    if(a >= 10) out(a / 10);
    putchar(a % 10 + '0');
}
void outl(ll a) {
    if(a < 0) { putchar('-'); a = -a; }
    if(a >= 10) outl(a / 10);
    putchar(a % 10 + '0');
}

const int N=107,M=25007;
int a[N];

//这里只需要考虑可不可以装满背包,所以用bool类型就可以啦
bool dp[M];

int main()
{
    int k;
    in(k);
    while(k--){
        int n,ans=0;
        in(n);
        for(int i=1;i<=n;i++)
        {
            in(a[i]);
        }
        sort(a+1,a+n+1);
        memset(dp,0,sizeof(dp));

		//前0种货币肯定可以表示出0货币
        dp[0]=1;
        for(int i=1;i<=n;i++)
        {
        	/*
        	这里的判断很关键!!!!!!!
        	在对a[i]货币进行决策之前,先判断一下此时的dp[a[i]]的状态;
        	这样我们就可以知道a[i]这种货币是否可以被它之前的货币表示出来.
        	*/
            if(!dp[a[i]])
                ans++;
            for(int j=a[i];j<=a[n];j++){
                dp[j] = dp[j] | dp[j-a[i]];
            }
        }
        out(ans);
        pl;
    }
    return 0;
}

下面我介绍一下我自己比较笨的方法,其实也是证明了为什么只要a[i]这种货币能够被之前的货币表示出来,就舍弃的原因.

首先,我们用数学语言来描述一下这道题目:

对于一个数列a=[a1,a2,..an],我们需要找到内部数据尽可能少的数列b=[b1,b2,...bm]

使得:
对于任意的M
	若存在n个整数k1,k2....kn,使得:
		k1*a1+k2*a2+...+kn*an=M
	则,必存在m个整数l1,l2....lm,同样使得:
		l1*b1+l2*b2+....+lm*bm=M
反之,则对于任意的m个整数,l1*b1+l2*b2+....+lm*bm都不可能等于M

所以,从上面的公式我们可以看出,k1a1+k2a2+…+kn*an就是对n个一维列向量组成的向量组a=[a1,a2,…an]进行线性组合.

然后,我们需要找到一个尽可能向量个数少的一维列向量组b,使得,两个列向量组可以相互线性表示,也就是等价.

学过线性代数的同学大概就看出来了,我们想要找的b就是向量组a的极大线性无关组.

根据极大线性无关组的定义,我们只要将a种所有可以被其他向量线性表示的向量去掉,剩下的就是a的极大线性无关组啦!!.

而我们完全背包的过程,就是不断去掉那些可以被其他向量线性表示的向量.

(现代的知识点本人有点模糊了,如果推导过程有错误,欢迎小伙伴们留言指出.O(∩_∩)O)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值