Hdu 6223 - Infinite Fraction Path - 暴力/bfs + 剪枝

题目链接

题目大意:

有n个城市,编号0 ~ n-1,每个城市权值为‘0’ ~ ‘9’,第i个城市可以到达第(i * i + 1) % n个城市,求经过n个城市的权值所构成长度为n的且字典序最大的字符串(1 <= n <= 150000)。

思路:

字典序最大需要保证在走到第i个城市时走所有能走的城市中权值最大的(0 <= i < n - 1)。

那么步骤就是:

  1. 计算当前所有能走的城市中的权值最大值maxx
  2. 将所有能走的城市中权值=maxx的城市下标记录下来
  3. ans[++ tot] = maxx;
  4. 重复上述步骤直到tot == n

我们可以使用^方便实现,当然我们也可以使用优先队列,让能走的城市中权值最大的排前,而不用每次筛出权值==maxx的城市,但是没什么必要,由于优先队列本身的复杂度,总的复杂度不一定更优。

如果只是这样,是不够的,因为我们只要让所有的城市权值都相等,就可让复杂度上升到n平方,显然会超时。

第i个城市可以到达(i * i + 1) % n个城市,取模会发生冲突像hash一样,而且由于本题的n很小,即hash表长小,并且如果权值==maxx的城市越多的话,冲突概率越大(剪枝效果越明显),比如x和y权值相同,但是x和y都走到z。

于是我们可以增加一个剪枝:每一层要走的权值==maxx的城市用v标记避免重复记录(即z只记录一次)

这个剪枝可以使TLE(3000ms) -> 187ms,在hdu6223 best solution中可以排第8。

代码:

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define mp make_pair
#define fi first
#define se second
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
typedef pair<ll, ll> pll;
const int mod = 1e9 + 7;
const int N = 15e4 + 10;
const int INF = 0x3f3f3f3f;
ll qpow(ll base, ll n){ll ans = 1; while (n){if (n & 1) ans = ans * base % mod; base = base * base % mod; n >>= 1;} return ans;}
ll gcd(ll a, ll b){return b ? gcd(b, a % b) : a;}
char s[N], ans[N];
int q[2][N], v[N];
int main()
{
	int t;
	int cas = 0;
	cin >> t;
	while (t --){
		int n;
		scanf("%d", &n);
		scanf("%s", s);
		int maxx = 0;
		for (int i = 0; i < n; ++ i) {
			maxx = max(maxx, (int)s[i]);
		}
		int cnt = 0, cur = 0;
		for (int i = 0; i < n; ++ i) {
			v[i] = 0;//标记最近使用时所在层数,用于剪枝
			if (s[i] == maxx) {
				q[cur][++ cnt] = i;
				v[i] = 1;
			}
		}
		int tot = 0, p;
		ans[++ tot] = maxx;
		while (tot < n){//搜索n层
			maxx = 0, p = 0;//每层的最大值初始为0,计数器清零
			for (int i = 1; i <= cnt; ++ i){
				int u = q[cur][i];
				u = (1ll * u * u + 1) % n;//计算下一个元素的下标
				maxx = max(maxx, (int)s[u]);//更新这层最大值
				q[cur ^ 1][++ p] = u;
			}
			ans[++ tot] = maxx;
			cur ^= 1, cnt = 0;
			for (int i = 1; i <= p; ++ i) {
				if (v[q[cur][i]] == tot) continue;//剪枝,当前层已经算过的不算
				if (s[q[cur][i]] == maxx) {//将值最大的下标放进队列,减小搜索规模
					v[q[cur][i]] = tot;//更新
					q[cur][++ cnt] = q[cur][i];//放进队列
				}
			}
		}
		ans[n + 1] = '\0';
		printf("Case #%d: %s\n", ++ cas, ans + 1);
	}
	return 0;
}

 

 

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值