【DP,求方案】AcWing 316. 减操作
分析
这题可以拆成两道题来做:
第一题
首先,看到这个合并顺序,感觉十分不好下手,那么我们不妨绕过对合并的分析,看看问题能等价为什么:
随便写几个柿子,比如 ,发现运算的结果可以化到最简表述为没有括号只有 +-
的形式,而且加减在除了第一个位置必然是减(因为最后合并的一定需要包括位置 )每个位置都可以出现,不妨大胆猜想:问题等价于——除了第一个位置,其他位置可以任意放置 +/-
。
下面是证明,感觉显然的话可以绕过。
证明:给你一个长度为 的序列,支持在中间的 个位置添加 +/-
,其中第一个一定为 -
,可以将其等价为支持选择邻项作减法的序列。
采用数学归纳法,记长度为 。
- 显然 时成立。
- 假设 时成立。
- 对于 ,由归纳假设,后缀 项能够表述为支持在中间的 个位置任意添加
+/-
,其中第一个一定为-
的序列。
为了方便表述,这里对 的情况(
A B C D E
)作解释:
首先A, B
间一定为-
:A - B C D E
。
对B C
之间的符号作分类讨论:
B + C
:那么我们就规定合并顺序为A - (B - C D E)
,可以发现后缀四项就是 的情况,满足归纳假设。B - C
:那么就率先合并A B
,情况化归为 。
可以发现上面的归纳过程正好能够为我们构造方案提供帮助。
第二题
背包 dp。
这题没什么需要注意的,就每个物品分别当成是正体积和负体积的 背包问题处理即可,当然因为数组下标非负,我们需要加一个足够大的偏移量,这里规定是 。
注意到需要输出方案,所以我们记录一下每个状态的前驱 pre
,然后利用 pre
得到 +-
序列并构造出解即可。(构造出解的思路可以见上面的证明)。
// Problem: 减操作
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/318/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include<bits/stdc++.h>
using namespace std;
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;
inline void read(int &x){
int s=0; x=1;
char ch=getchar();
while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
x*=s;
}
const int N=110, M=20005, del=10000;
int n, V;
int w[N];
bool f[N][M];
pii pre[N][M];
bool in(int val){
return val>=0 && val<M;
}
string str;
void get_str(int x, int y){
if(x==1) return;
auto [tx, ty]=pre[x][y];
get_str(tx, ty);
if(ty<y) str+="+";
else str+="-";
}
void print(int u, int d, bool fl){
if(u==n) return;
if(u+1==n){
cout<<u-d<<endl;
return;
}
bool nx=(str[u+1]=='+')^fl;
if(nx) print(u+1, d, fl^1), cout<<u-d<<endl;
else cout<<u-d<<endl, print(u+1, d+1, fl);
}
int main(){
cin>>n>>V;
rep(i,1,n) read(w[i]);
f[1][w[1]+del]=1;
rep(i,2,n){
int t=w[i];
if(i>2){
rep(j,0,M-1) if(in(j-t) && !f[i][j] && f[i-1][j-t]){
f[i][j]=1;
pre[i][j]={i-1, j-t};
}
}
t=-t;
rep(j,0,M-1) if(in(j-t) && !f[i][j] && f[i-1][j-t]){
f[i][j]=1;
pre[i][j]={i-1, j-t};
}
}
get_str(n, V+del);
str=" "+str;
print(1, 0, 0);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?