【DP,求方案】AcWing 316. 减操作

分析

这题可以拆成两道题来做:

第一题

首先,看到这个合并顺序,感觉十分不好下手,那么我们不妨绕过对合并的分析,看看问题能等价为什么:

随便写几个柿子,比如 \(1-((1-4)-(5-(1-4)))\),发现运算的结果可以化到最简表述为没有括号只有 +- 的形式,而且加减在除了第一个位置必然是(因为最后合并的一定需要包括位置 \(1\))每个位置都可以出现,不妨大胆猜想:问题等价于——除了第一个位置,其他位置可以任意放置 +/-

下面是证明,感觉显然的话可以绕过。

证明:给你一个长度为 \(n\) 的序列,支持在中间的 \(n-1\) 个位置添加 +/-,其中第一个一定为 -,可以将其等价为支持选择邻项作减法的序列。
采用数学归纳法,记长度为 \(k\)

  • 显然 \(k = 2, 3\) 时成立。
  • 假设 \(k = n\) 时成立。
  • 对于 \(k = n + 1\),由归纳假设,后缀 \(n\) 项能够表述为支持在中间的 \(n-1\) 个位置任意添加 +/-,其中第一个一定为 -的序列。

为了方便表述,这里对 \(k=5\) 的情况(A B C D E)作解释:
首先 A, B 间一定为 -A - B C D E
B C 之间的符号作分类讨论:

  1. B + C:那么我们就规定合并顺序为 A - (B - C D E),可以发现后缀四项就是 \(k=4\) 的情况,满足归纳假设。
  2. B - C:那么就率先合并 A B,情况化归为 \(k = 4\)

可以发现上面的归纳过程正好能够为我们构造方案提供帮助。

第二题

背包 dp。
这题没什么需要注意的,就每个物品分别当成是正体积和负体积的 \(01\) 背包问题处理即可,当然因为数组下标非负,我们需要加一个足够大的偏移量,这里规定是 \(100\times 100\)
注意到需要输出方案,所以我们记录一下每个状态的前驱 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;
}
posted @ 2022-05-05 16:30  HinanawiTenshi  阅读(37)  评论(0编辑  收藏  举报