【补题】The 1st Universal Cup. Stage 15: Hangzhou D. Master of Both III

题意:太复杂了,大概是给出一个n,然后问集合【0,1,2,......,n-1】按照计算得到的答案是什么。

原题:Master of Both III - Problem - QOJ.ac

思路:The 1st Universal Cup. Stage 15: Hangzhou - 空気力学の詩 - 博客园

1.问什么情况到什么情况的最小消耗,很容易想到是最短路,而且n给了22,这给了状态压缩,也就是把状态看成一个数值的机会。可以直接转化成很多的点,在图中有着n条边(包括0自环)。

2.问任何情况变成0,其实倒过来就是0跑到任何情况的最短路是什么。因此问题转化为由0出发,跑到别的状态是什么样的。

来到样例,2 1 2,在状态子集有1 2的情况下,最短是1走到2,然后2一起走到0。

这个意思就是别的点会走到一起重合,所以由0出发的询问应当保留自身(重合)

cpp 复制代码
int check(int x,int p){
	x |= (x << (n - p)) | (x >> p);
	x &= (1ll << n) - 1;
	return x;
}

非常好的代码,其实就是包括了2->0(循环),或者0->1(直接)两种可能,然后用&操作就区分开了,不懂可以分别看(x << (n - p)) 和 (x >> p)。

cpp 复制代码
int check(int x,int p){
	x |= (x << (n - p)) | (x >> p);
	x &= (1ll << n) - 1;
	return x;
}
for (int i = (1ll << n) - 1; i > 0;i--){
		for (int j = 0; j < n;j++){
			if(!((i>>j)&1)){
				dp[i] = min(dp[i | (1ll << j)], dp[i]);
			}
		}
	}

就是由0开始的,任何试图重合或者直接走到的计算(倒着走的)。

看着像dp一样,那么每个点实际一定是最小值,前者一定会判到

因为是重合的计算,所以当前数字一定由少一位1得来,否则不会在这个for循环中计算。

是满足由0出发的最短路的,也满足dp的前置都是完全计算的。

但是这还不够因为还有10110这种存在,起始状态没有0

cpp 复制代码
for (int i = (1ll << n) - 1; i > 0;i--){
		for (int j = 0; j < n;j++){
			if(!((i>>j)&1)){
				dp[i] = min(dp[i | (1ll << j)], dp[i]);
			}
		}
	}

一样的,剩下状态一定能按照之前覆盖过的状态的计算方式回到0,因此直接同步最小状况即可。

但是为了满足dp的性质,它应当是由大数走向低数,因为这样才能满足当前状态比它更丰满的状态是被正确计算完成的。

需要满足dp前置完成的性质。

那么至此所有状态都成功由0覆盖而来,完成了将所有状态压缩成点,最短路的询问,最后其实就是i这个状态的值的大小

代码:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define int128 __int128
#define endl '\n'
#define IOS                  \
	ios::sync_with_stdio(0); \
	cin.tie(0);              \
	cout.tie(0);
const int N = 2e5 + 10;
const int INF = 1e18;
const int MOD = 998244353;

int cost[35];
int dp[(1ll<<22)];
int n;

int check(int x,int p){
	x |= (x << (n - p)) | (x >> p);
	x &= (1ll << n) - 1;
	return x;
}

void solve(){
	cin >> n;
	for (int i = 0; i < n;i++){
		cin >> cost[i];
	}

	memset(dp, 0x3f3f3f3f, sizeof(dp));

	dp[1] = 0;
	for (int i = 1; i < (1ll << n);i++){
		int idx = i;
		for (int j = 1; j < n;j++){
			dp[check(idx, j)] = min(dp[check(idx, j)], dp[idx] + cost[j]);
		}
	}

	for (int i = (1ll << n) - 1; i > 0;i--){
		for (int j = 0; j < n;j++){
			if(!((i>>j)&1)){
				dp[i] = min(dp[i | (1ll << j)], dp[i]);
			}
		}
	}

	int ans = 0;
	for (int i = 1; i < (1ll << n); i++){
		ans = (ans + (dp[i] * i % MOD)) % MOD;
	}
	cout << (ans + MOD) % MOD << endl;
}

signed main()
{
	//	IOS;

	int t = 1;
	// cin >> t;
	while (t--)
	{
		solve();
	}
}

非常优美的代码,利用类似dp的计算方式就完成了所有状态的计算。
本人瞎贪心,自己做的时候以为所有点按照自己最短方式走到0就是最好的,写成同余最短路了......

相关推荐
Fcy64816 分钟前
算法基础详解(五)二分算法——二分查找与二分答案
算法·二分算法
SteveSenna34 分钟前
强化学习4.1:基于价值——Q-learning
人工智能·学习·算法·机器人
少许极端35 分钟前
算法奇妙屋(四十四)-贪心算法学习之路11
java·学习·算法·贪心算法
子琦啊35 分钟前
【算法复习】数组与双指针篇
javascript·算法
ambition2024238 分钟前
斐波那契取模问题的深入分析:为什么提前取模是关键的
c语言·数据结构·c++·算法·图论
逆境不可逃1 小时前
LeetCode 热题 100 之 230. 二叉搜索树中第 K 小的元素 199. 二叉树的右视图 114. 二叉树展开为链表
算法·leetcode·职场和发展
一个有温度的技术博主1 小时前
Redis Cluster 核心原理:哈希槽与数据路由实战
redis·算法·缓存·哈希算法
wfbcg1 小时前
每日算法练习:LeetCode 15. 三数之和 ✅
算法·leetcode·职场和发展
2301_822703201 小时前
开源鸿蒙跨平台Flutter开发:跨端图形渲染引擎的类型边界与命名空间陷阱:以多维雷达图绘制中的 dart:ui 及 StrokeJoin 异常为例
算法·flutter·ui·开源·图形渲染·harmonyos·鸿蒙
y = xⁿ1 小时前
【LeetCode Hot100】双指针:分离指针
算法·leetcode