蓝桥杯砝码称重

解法一

自己最开始写的dfs搜索,时间复杂度太高,为 O ( 3 n ) O(3^n) O(3n),只过了一半的数据。基本思想是枚举每一个砝码,每个砝码有三种状态,放左边,放右边,不放,放左边记为减法,放右边记为加法,依次枚举并对出现的所有结果用一个数组去重。

#include 
#include 
#include 
#define ll long long
using namespace std;
const int maxn = 1e5 + 1;
int s[maxn] = { 0 };
int n, a[101] = { 0 };
ll ans = 0;

void dfs(int index, int sum) {
    if (index > n + 1) {
        return;
    }
    int k = fabs(sum);
    if (k != 0 && s[k] == 0) {
        s[k] = 1;
        ans++;
    }
    //放左边
    dfs(index + 1, sum - a[index]);
    //放右边
    dfs(index + 1, sum + a[index]);
    //不放
    dfs(index + 1, sum);
}

int main()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    dfs(1, 0);
    cout << ans;
    return 0;
}

解法二:

使用bfs遍历所有的结果的情况,虽然基本思想和dfs是相似的,但是时间复杂度却不一样,此种解法在最坏的情况下应该是每次出现的结果都没被访问过,时间复杂度应该是 O ( n 2 ) O(n^2) O(n2),我个人认为问题就出在了讨论每个砝码的三种状态这一步。

#include 
#include 
#include 
#define ll long long
using namespace std;
const int maxn = 1e5 + 1;
int s[maxn] = { 0 };
int n, ans = 0;
queue<int> q;

int main()
{
    cin >> n;
    q.push(0);
    for (int i = 1; i <= n; i++) {
        int w; cin >> w;
        //tmp_q可理解为一个备份
        queue<int> tmp_q;
        //q中存放当前已经遍历到的所有结果,将w与q中所有元素依次进行放左放右以及不放的操作
        while (!q.empty()) {
            int tmp = q.front();
            q.pop();
            //放右边
            if (!s[tmp + w]) {
                s[tmp + w] = 1;
                ans++;
                tmp_q.push(tmp + w);
            }
            //放左边
            int abs = fabs(tmp - w);
            if (!s[abs]) {
                s[abs] = 1;
                ans++;
                tmp_q.push(abs);
            }
            //不放
            tmp_q.push(tmp);
        }
        q = tmp_q;
    }
    //重量为0的情况不考虑,即两边砝码一样重时,虽然题目好像没说
    if (s[0] == 1)
        ans--;
    cout << ans;
    return 0;
}

解法三:

动态规划解法不多说,直接说一下状态转移方程:
d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + d p [ i − 1 ] [ a b s ( j − a [ i ] ) ] + d p [ i − 1 ] [ j + a [ i ] ] + ( a [ j ] = = j ) dp[i][j]=dp[i-1][j]+dp[i-1][abs(j-a[i])]+dp[i-1][j+a[i]]+(a[j]==j) dp[i][j]=dp[i1][j]+dp[i1][abs(ja[i])]+dp[i1][j+a[i]]+(a[j]==j)

  • 此处的 " + " "+" "+"为‘或’的意思
  • d p [ i ] [ j ] dp[i][j] dp[i][j]的意义为使用前 i i i个砝码是否能够得到重量j
  • 使得 d p [ i ] [ j ] dp[i][j] dp[i][j]为真,下列条件有一条成立即可:
    • d p [ i − 1 ] [ j ] dp[i-1][j] dp[i1][j]为真,原因是使用前 i − 1 i-1 i1个砝码即可得到重量 j j j ,则使用前 i i i个当然也可以
    • d p [ i − 1 ] [ a b s ( j − a [ i ] ) ] dp[i-1][abs(j-a[i])] dp[i1][abs(ja[i])] d p [ i − 1 ] [ j + a [ i ] ] dp[i-1][j+a[i]] dp[i1][j+a[i]]为真,原因是此时第 i i i个砝码恰好可以与前 i − 1 i-1 i1个砝码配合得到重量 j j j
    • a [ i ] = = j a[i]==j a[i]==j为真,原因是即便前 i − 1 i-1 i1个砝码都不能得到重量 j j j,但有第 i i i个也足矣
#include 
#include 
using namespace std;
const int maxn = 1e5 + 1;
//动态规划解法
int n, cnt = 0, a[101], dp[101][maxn] = { 0 };

int main() {
    cin >> n;
    int sum = 0;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        sum += a[i];
    }
    dp[1][a[1]] = 1; cnt = 1;
    for (int i = 2; i <= n; i++) {
        //继承上一轮的结果
        for (int j = 1; j <= sum; j++) {
            dp[i][j] = dp[i - 1][j];
        }
        for (int j = 1; j <= sum; j++) {
            if (dp[i][j] == 0) {
                int abs = fabs(j - a[i]);
                dp[i][j] = (a[i] == j) | dp[i - 1][abs] | dp[i - 1][j + a[i]];
                if (dp[i][j] == 1)
                    cnt++;
            }
        }
    }
    cout << cnt;
    return 0;
}

解法四:

纯数据结构解法,无算法

#include 
#include 
#include 
#include 
using namespace std;
int n;
set<int> s;

int main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int t; cin >> t;
        vector<int> a(s.begin(), s.end());
        for (int i = 0; i < a.size(); i++) {
            s.insert(a[i] + t);
            int abs = fabs(a[i] - t);
            if (abs != 0) {
                s.insert(abs);
            }
        }
        s.insert(t);
    }
    cout << s.size();
    return 0;
}

你可能感兴趣的:(蓝桥杯,深度优先,算法)