题解 [CF1342F] Make It Ascending

传送门

为啥在沈老师眼里这是一道巨大无意义题啊

首先有一个 \(O(4^n)\) 级别的暴力
\(f_{i, s, t}\) 为当前考虑到位置 \(i\),递增序列中上个元素由 \(s\) 组成,已知要跨过/停留在 \(i\) 的元素集合为 \(t\) 的最小操作次数
转移枚举 \(t\) 的子集作为递增序列中的一个新元素,或者将 \(i\) 扔进 \(t\)

然后发现这样光状态就是 \(3^n\) 级别的了,考虑优化

  • 当状态数较大而 DP 值较小/状态数与值域相关而 DP 值与值域无关且较小时:考虑交换状态与 DP 值

那么对应到本题:
换一种 DP 方式:
\(f_{i, j, s}\) 为已向递增序列中加入了 \(i\) 个元素,
\(s\) 元素未加入序列时,序列中上个元素最终集中于原序列中位置 \(j\),序列中上个元素的权值最小值
复杂度 \(O(n^33^n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define fir first
#define sec second
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
pair<int, int> g[16][16][1<<15];
int a[N], f[16][16][1<<15], val[1<<15], id[16];

int pos(int s, int k) {return __builtin_popcount(s&((1<<(k-1))-1))+1;}

int dfs(int i, int j, int s) {
	// cout<<"dfs: "<<i<<' '<<j<<' '<<bitset<8>(s)<<endl;
	if (!i) return 0;
	pair<int, int> back=g[i][j][s];
	int t=back.sec^s, now=back.sec, mask=dfs(i-1, back.fir, back.sec);
	now|=mask;
	// cout<<"s: "<<bitset<8>(back.sec)<<' '<<bitset<8>(s)<<endl;
	for (int k=1; k<=n; ++k) if (k!=j && t&(1<<(k-1))) {
		printf("%d %d\n", pos(now, k), pos(now, j));
		now^=1<<(k-1);
	}
	return mask|(1<<(j-1));
}

signed main()
{
	int T=read();
	while (T--) {
		n=read();
		for (int i=0; i<n; ++i) a[i]=read();
		int lim=1<<n;
		for (int i=0; i<=n; ++i) for (int j=0; j<=n; ++j) for (int s=0; s<lim; ++s) f[i][j][s]=INF;
		for (int s=0; s<lim; ++s) {
			val[s]=0;
			for (int i=0; i<n; ++i)
				if (s&(1<<i)) val[s]+=a[i];
		}
		f[0][0][lim-1]=0;
		for (int i=0; i<n; ++i) {
			for (int j=0; j<=n; ++j) {
				for (int s=1; s<lim; ++s) if (f[i][j][s]!=INF) {
					for (int t=s; t; t=(t-1)&s) if (val[t]>f[i][j][s]) {
						for (int k=j+1; k<=n; ++k) if (t&(1<<(k-1))) {
							if (val[t]<f[i+1][k][s^t]) {
								f[i+1][k][s^t]=val[t];
								g[i+1][k][s^t]={j, s};
							}
						}
					}
				}
			}
		}
		for (int i=n; i; --i) {
			for (int j=1; j<=n; ++j) if (f[i][j][0]!=INF) {
				printf("%d\n", n-i);
				dfs(i, j, 0);
				goto jump;
			}
		}
		jump: ;
	}
	
	return 0;
}
posted @ 2022-04-25 16:51  Administrator-09  阅读(5)  评论(0编辑  收藏  举报