acwing 316 减操作(dp)

题目链接

题目大意

  略

解题思路

  最终的结果可以用数字,减号与括号表示,如果把所有的括号去掉,就得到了一个由数字,加法与减法组成的式子,可以想到,对于每一个数字,都有加上它或者减去它两种情况,很明显可以用01背包来解决。需要注意的一点是,第一个数字前头没有符号,而第二个数字肯定是被减的。
  那么怎么还原操作顺序呢?一个数字前面是加号,那他前面肯定存在一个数字前面是减号,比如-a+b+c+d这样,可以转化为-(((a-b)-c)-d),那么我们可以先执行a-b, a-c, a-d,最后再-a就可以了,所以对于每一个前面是加号的数字,我们可以找一个前面里它最近的前面是减号的数字作为答案。可以发现如果我们正着做的话,在删除的过程中由于前面的数字少了,当前的数字的序号会发生改变,如果我们倒着来做的话,即使删除了后面的数字前面的序号也不会改变,写起来也简单。最后剩下一些前面是减号的数字,用第一个数字来减它们就行了。

代码

#include<bits/stdc++.h>
#define endl '\n'
#define x first
#define y second
#define clr(arr,a) memset(arr, a, sizeof(arr))
#define IOS ios::sync_with_stdio(false)
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll, ll> P;
typedef pair<int, int> Pll;
const double pi = acos(-1.0);
const double eps = 1e-8;
const int MOD = 1e9+7;
const int INF = 0x3f3f3f3f;
const int maxn = 1e2+10;
const int maxm = 2e4+10;
int n, t, arr[maxn], dp[maxn][maxm], op[maxn];
int main() {
    cin >> n >> t;
    for (int i = 1; i<=n; ++i) cin >> arr[i];
    dp[2][10005+arr[1]-arr[2]] = -1;
    for (int i = 3; i<=n; ++i)
        for (int j = maxm; j>=0; --j) 
            if (dp[i-1][j]) {
                if (j+arr[i]<maxm) dp[i][j+arr[i]] = 1;
                if (j-arr[i]>=0) dp[i][j-arr[i]] = -1;
            }
    int now = 10005+t;
    for (int i = n; i>=2; --i) {
        if (dp[i][now]>0) now -= arr[i], op[i] = 1;
        else now += arr[i], op[i] = -1;
    }
    vector<int> ans;
    int p;
    for (int i = 2; i<=n; ++i) {
        if (op[i]<0) p = i;
        else ans.push_back(p);
    }
    reverse(ans.begin(), ans.end());
    for (int i = 2; i<=n; ++i)
        if (op[i]<0) ans.push_back(1);
    for (auto v : ans) cout << v << endl;
    return 0;
}
posted @ 2021-04-17 21:02  shuitiangong  阅读(73)  评论(0编辑  收藏  举报