2022 高考集训
Updating!
虽然说题目叫 2022 高考集训然而我并不集训 233,我只负责补题 QwQ
fengwu 大爷说不要放题面,遂不放了 .
没代码的就是口胡,不用管就行 .
缺省源大概是
#include <bits/stdc++.h>
template<typename T>
inline T chkmin(T& x, const T& y){if (x > y) x = y; return x;}
template<typename T>
inline T chkmax(T& x, const T& y){if (x < y) x = y; return x;}
#define file(x) {freopen(x".in", "r", stdin); freopen(x".out", "w", stdout);}
#define twicecat(p, q) p##p##q
#define twiceline(t) twicecat(_, t)
Day 1
A. 2A
模拟即可,先扫一遍给出的字符串,然后提出四个数字来就好做了 .
如果满足格式就等价于改完的 IP 和原来的一模一样 .
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
string s;
int main()
{
file("ip");
cin >> s; int len = s.length();
int num[4] = {}, cc = 0;
for (int i=0; i<len; i++)
if (isdigit(s[i])){num[cc] = num[cc] * 10 + s[i] - '0'; chkmin(num[cc], 255);}
else if (i && isdigit(s[i-1])) ++cc;
char ans[114514];
sprintf(ans, "%d.%d.%d.%d", num[0], num[1], num[2], num[3]);
if (!strcmp(ans, s.c_str())) puts("YES");
else puts("NO"), cout << ans << endl;
return 0;
}
B. 2B
非常显然的一个贪心,因为要最后留下的字符最少,所以肯定要多做操作 .
于是我们尽量多做 AP 再做 PP,然而我们注意到字符集是 \(\{\mathtt A,\mathtt P\}\),于是这个操作其实相当于把 P 前面的字符删掉,所以扫一遍整个字符串,用一个栈维护,遇到 P 就弹即可 .
(其实这里栈可以省掉,就等价于 Eafoo 的 DP 做法了)
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
string s;
stack<char> S;
int main()
{
file("apstr");
cin >> s; int n = s.length(); s = "$" + s;
for (int i=1; i<=n; i++)
{
if ((s[i] == 'P') && (!S.empty())) S.pop();
else S.push(s[i]);
} printf("%d", int(S.size()));
return 0;
}
C. 2C
wobuhuizuo
D. 2D
应该确保 \(k<0\) .
(结果样例 \(k=2\),迫真)
开始胡的是从 \(k\) 小的往 \(k\) 大的推,这样不太能做,等价于 Eafoo 的 \(n^2\) 过百万 拓扑排序算法 .
开始复读题解:
首先 \((k+1)\)-degree 一定是 \(k\)-degree 的子图 .
于是我们从大到小枚举 \(k\),每次扩展原图即可 .
如果一个点属于 \((k+1)\)-degree 但不属于 \(k\)-degree 我们就称其在第 \(k+1\) 层,记 \(D(x)\) 为 \(x\) 的层数 .
我们按照层数从大到小加入点,考虑计算每次加入点引起的分数贡献 .
首先肯定是分顶点数 \(n\),边数 \(m\),边界边数 \(b\) 计算贡献 .
在点集上定义一个偏序 \(\prec\)(对应到题解中就是给每个点一个 \(rank\)),满足:
- 若 \(D(x)<D(y)\),则 \(x\prec y\) .
- 若 \(D(x)=D(y)\) 且 \(x<y\),则 \(x\prec y\) .
可以看出,这就是层数的比较 . 另一方面,这是双关键字排序,可以通过 计数排序 桶排序(也就是所谓 bin-sort)完成 .
于是我们可以通过边上两点 \(\prec\) 来对一条边进行分类:
- 记 \(E(u,\prec)\) 表示所有边 \(u\to v\) 中满足 \(u\prec v\) 的个数 .
- 记 \(E(u,\succ)\) 表示所有边 \(u\to v\) 中满足 \(v\prec u\) 的个数 .
- 记 \(E(u,=)\) 表示所有边 \(u\to v\) 中不满足上面两种情况的个数(等价于 \(D(u)=D(v)\)) .
于是每次加一个点 \(u\),给 \(n,m,b\) 的影响分别是:
- \(\Delta n=1\) .
- \(\Delta m = E(u,\prec)+\dfrac 12E(u,=)\) .
- \(\Delta b = E(u,\prec)-E(u,\succ)\) .
然而实现的时候可以不用这么麻烦 就这样从小到大加,统计 \(score\) 然后取 min 即可 .
然后做的时候子图还可能被边连起来(合并),所以还需要并查集维护一下 .
关于时间复杂度,题解说是 \(O(n+m)\),感觉如果题解您真做到这个复杂度不得图灵奖,,,肯定要乘个反 Ackermann 函数吧 233
太难写了,SMYwy 大佬的做法我没看懂,于是贺 lyin 的代码跑路了 .
说的不够完善的地方结合代码理解吧 .
lyin 大佬的代码
#include <bits/stdc++.h>
#define fre(x) freopen( #x ".in", "r", stdin), freopen( #x ".out", "w", stdout )
using namespace std; typedef long long ll; typedef unsigned long long ull; typedef double db; typedef long double ldb;
const int N = 1e6 + 10;
mt19937 mt( (ull)(new char) );
int Rand ( int l, int r ) { return uniform_int_distribution<>(l,r)(mt); }
int n, m, kn, km, kb, du[N]; vector <int> vec[N];
struct BCJ
{
int fa[N], sm[N], sn[N], sd[N];
void Init ( int n ) { for( int i = 1; i <= n; ++i) fa[i] = i, sm[i] = 0, sn[i] = 1, sd[i] = du[i]; }
int Find ( int x ) { return x == fa[x] ? x : fa[x] = Find(fa[x]); }
void Merge ( int x, int y )
{
int fx = Find(x), fy = Find(y); if( fx == fy ) return; if( sn[fx] > sn[fy] ) swap( fx, fy );
fa[fx] = fy, sm[fy] += sm[fx], sn[fy] += sn[fx], sd[fy] += sd[fx];
}
ll Calc ( int x ) { return sn[x] == 1 ? -LLONG_MAX : 0LL + 1LL * km * sm[x] + 1LL * kn * sn[x] + 1LL * kb * ( sd[x]-2LL*sm[x] ); }
}S;
struct Graph
{
struct Edge { int to, next; } E[N<<1];
int ind, head[N];
void Insert ( int u, int v ) { ++ind, ++du[u], E[ind].to = v, E[ind].next = head[u], head[u] = ind; }
void Link ( int u, int v ) { Insert( u, v ), Insert( v, u ); }
vector <int> bin[N]; bool vis[N]; int top;
bool Select ( int k, int &u ) { while( bin[top].empty() && top <= k ) ++top; if( top > k ) return false; u = bin[top][ bin[top].size()-1 ], bin[top].pop_back(); return true; }
void Solve ()
{
for( int i = 1; i <= n; ++i) bin[ du[i] ].push_back( i ); top = 0;
for( int k = 1, u = 0; k <= n; ++k) while( Select( k, u ) )
{
if( vis[u] ) continue; vec[k].push_back(u), vis[u] = true, S.sm[u] = top;
for( int i = head[u]; i; i = E[i].next ) if( !vis[ E[i].to ] ) bin[ --du[ E[i].to ] ].push_back( E[i].to ), top = min( top, du[ E[i].to ] );
}
for( int i = 1; i <= n; ++i) vis[i] = 0;
ll maxi = -LLONG_MAX, maxk = 0;
for( int k = n; k >= 1; --k)
{
reverse( vec[k].begin(), vec[k].end() );
for( auto u : vec[k] ) { vis[u] = true; for( int i = head[u]; i; i = E[i].next ) if( vis[ E[i].to ] ) S.Merge( u, E[i].to ); }
for( auto u : vec[k] ) if( maxi < S.Calc( S.Find(u) ) ) maxi = S.Calc( S.Find(u) ), maxk = k;
}
cout << maxk << " " << maxi << "\n";
}
}G;
void Solve ()
{
cin >> n >> m >> km >> kn >> kb, kn = -kn;
for( int i = 1; i <= m; ++i) { int a, b; cin >> a >> b, G.Link( a, b ); }
S.Init(n), G.Solve();
}
signed main ()
{
fre(kdgraph);
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); Solve(); return 0;
}
我的代码
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 1e6 + 233;
const ll INF = 0x7f7f7f7f7f7f7f7fll;
vector<int> g[N];
int d[N];
inline void addedge(int u, int v){g[u].emplace_back(v); ++d[u];}
inline void ade(int u, int v){addedge(u, v); addedge(v, u);}
int n, m, mm, nn, bb;
struct dsu
{
int fa[N];
ll dm[N], dn[N], dd[N];
inline void reset(int n){for (int i=1; i<=n; i++){fa[i] = i; dm[i] = 0; dn[i] = 1; dd[i] = d[i];}}
int get(int x){return x == fa[x] ? x : fa[x] = get(fa[x]);}
void merge(int x, int y)
{
x = get(x); y = get(y);
if (x == y) return ;
if (dn[x] > dn[y]) swap(x, y); //
fa[x] = y; dm[y] += dm[x]; dn[y] += dn[x]; dd[y] += dd[x];
}
ll score(int x){x = get(x); return dn[x] == 1 ? -INF : dm[x] * mm + dn[x] * nn + (dd[x] - 2 * dm[x]) * bb;}
}D;
vector<int> lev[N], bin[N];
bool vis[N];
int top;
bool select(int k, int& u) // 找一个 k 层点 u
{
while (bin[top].empty() && (top <= k)) ++top;
if (top > k) return false;
u = bin[top].back(); bin[top].pop_back();
return true;
}
inline void solve()
{
D.reset(n); int u = 0;
for (int i=1; i<=n; i++) bin[d[i]].emplace_back(i);
for (int k=1; k<=n; k++)
while (select(k, u))
{
if (vis[u]) continue;
vis[u] = true;
lev[k].emplace_back(u); D.dm[u] = top;
for (int v : g[u])
if (!vis[v]){bin[--d[v]].emplace_back(v); chkmin(top, d[v]);}
}
memset(vis, false, sizeof vis);
ll maxn = -INF, k = 114514;
for (int i=n; i>=1; i--)
{
reverse(lev[i].begin(), lev[i].end());
for (int u : lev[i])
{
vis[u] = true;
for (int v : g[u])
if (vis[v]) D.merge(u, v);
}
for (int u : lev[i])
{
ll _ = D.score(u);
if (maxn < _){maxn = _; k = i;}
}
} printf("%lld %lld\n", k, maxn);
}
int main()
{
file("kdgraph");
scanf("%d%d%d%d%d", &n, &m, &mm, &nn, &bb); nn *= -1;
for (int i=0, u, v; i<m; i++) scanf("%d%d", &u, &v), ade(u, v);
solve();
return 0;
}
简直一模一样!!!说是 CV 的都不为过!!!!
Day 2
A. 交通
把每个点的两个出边、两个入边连边,则我们的要求就可以变成在新图上选 \(n\) 个不相邻的点 .
容易发现这个图每个点的度数都是 \(2\) 且只有偶环,于是答案就是 \(2^{cycle}\),其中 \(cycle\) 是环数(此时也是连通块个数),并查集即可 .
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 5e5 + 500, P = 998244353;
int n, tin[N], tout[N];
int qpow(int a, int n)
{
int ans = 1;
while (n)
{
if (n & 1) ans = 1ll * ans * a % P;
a = 1ll * a * a % P; n >>= 1;
} return ans;
}
struct dsu
{
int fa[N];
dsu(){iota(fa, fa+N, 0);}
int get(int x)const{return x == fa[x] ? x : get(fa[x]);}
inline void merge(int u, int v){fa[get(u)] = get(v);}
inline int blocks()const
{
int ans = 0;
for (int i=1; i<=n; i++) ans += (i == fa[i]);
return ans;
}
}D;
int main()
{
file("a");
scanf("%d", &n); n <<= 1;
for (int i=1, u, v; i<=n; i++)
{
scanf("%d%d", &u, &v);
if (tin[v]) D.merge(i, tin[v]);
if (tout[u]) D.merge(i, tout[u]);
tin[v] = tout[u] = i;
} printf("%d\n", qpow(2, D.blocks()));
return 0;
}
B. 冒泡排序
观察题目这个冒泡 . 可以讨论 \(a_i\) 要往哪边冒,这样就把问题细化到点上了
设 \(p\) 是题目里给的 \(p\) 的置换逆 .
如果 \(a_i>i\),那么必须往右边冒,那么我们就要求后面的数必须后冒,于是就会发现相邻位置的交换顺序有 一些限制,限制形如某对相邻的交换必须在它旁边的相邻对交换之前 / 之后,\(a_i<i\) 也类似 .
这个可以大于小于分开处理就变成区间加求前缀和,差分即可 .
现在问题就是给一堆形如 \(p_i>p_{i-1}\) 或 \(p_i<p_{i-1}\) 的限制,让你求满足条件的排列 \(\{p\}\) 的个数,直接 \(O(n^2)\) DP 即可 .
应该能做到更优复杂度 desu(题解说 容斥 + 分治 FFT 可以 \(O(n\log ^2n)\) .jpg)
具体说一下这个 DP,大概是排列 DP 的经典 Trick,有点像 地精部落 .
令 \(dp_{i,j}\) 表示处理到第 \(i\) 个数且有 \(j\) 个大于位置 \(i\) 放的数 .
顺推做法大概就是:如果 \(p_{i+1}>p_i\) 那么就有 \(j\) 种方案可以放在 \(i+1\),\(p_{i+1}<p_i\) 同理,无约束就直接把两种情况并起来,然后因为加的是区间所以差分优化一下就好了,\(O(n^2)\) .
逆推一样就是把差分换成前缀和,好实现一点,\(O(n^2)\) .
第一维可以滚一下,空间复杂度就是 \(O(n)\) 的了 .
UPD: DP 部分是 [HNOI2015] 实验比较 弱化版,并且原题存在 \(O(n^2)\) 做法 .
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 5555, P = 1e9 + 7;
int n, dp[2][N], a[N], b[N];
int main()
{
file("mp");
scanf("%d", &n);
for (int i=1, x; i<=n; i++)
{
scanf("%d", &x); ++x;
if (x == i){puts("0"); return 0;}
if (x > i){++a[i]; --a[x-1];}
else{++b[x]; --b[i-1];}
} --n;
for (int i=1; i<=n; i++)
{
a[i] += a[i-1]; b[i] += b[i-1];
if (a[i] && b[i]){puts("0"); return 0;}
}
for (int i=1; i<=n; i++) dp[1][i] = 1;
int lst = 1;
for (int i=2; i<=n; i++)
{
bool _P = a[i-1], _Q = b[i-1], _R = !_P && !_Q, now = !lst;
if (_P || _R) for (int j=2; j<=i; j++) dp[now][j] = (dp[now][j-1] + dp[lst][j-1]) % P;
if (_Q || _R) for (int j=i-1; j; j--) dp[now][j] = (dp[now][j+1] + dp[lst][j]) % P;
memset(dp[lst], 0, sizeof dp[lst]);
lst ^= 1;
} int ans = 0;
for (int i=1; i<=n; i++) ans = (ans + dp[n&1][i]) % P;
printf("%d\n", ans);
return 0;
}
C. 矩阵
一道牛逼题,结论是只需要把前两行和前两列归零,如果此时还不能把整个矩阵归零那么无解 .
虽然我并不会证 desu(UPD:GCC 定理
这样就随便做了 .
参考代码
蒯的 std
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1234;
int n, m, a[N][N];
ll line[N], row[N], diag[2 * N];
int main()
{
file("c");
scanf("%d%d", &n, &m);
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++) scanf("%d", a[i] + j);
line[1] = a[2][2] - a[1][1]; diag[n] = -a[2][2];
for (int i=2; i<=n; i++){diag[n-i+1] = -(a[i][1] + line[i]); line[i+1] = -(a[i+1][2] + diag[n-i+1]);}
for (int i=2; i<=m; i++){diag[n+i-1] = -(a[1][i] + line[1] + row[i]); row[i+1] = -(a[2][i+1] + diag[n+i-1]);}
for (int i=1; i<=n; i++)
for (int j=1; j<=m; j++)
if (a[i][j] + line[i] + row[j] + diag[j-i+n]){puts("-1"); return 0;}
printf("%d\n", 2*(n+m) - 1);
for (int i=1; i<=n; i++) printf("1 %d %lld\n", i, line[i]);
for (int i=1; i<=m; i++) printf("2 %d %lld\n", i, row[i]);
for (int i=1; i<=n+m-1; i++) printf("3 %d %lld\n", i-n, diag[i]);
return 0;
}
D. 花瓶
先口胡,代码以后补 .
比较显然的斜率优化 DP,类似 HDU2829 Lawrence .
然而这是一个 2D 的斜率优化 DP,这里总结一下 Trick .
设 \(s\) 是序列前缀和,令 \(dp_{i,j}\) 表示目前分到第 \(i\) 个花瓶,上一个段的右端点为 \(j\),于是转移为
这样是 \(O(n^3)\) 的,斜率优化掉就是 \(O(n^2)\) 的了 .
然而 Keven_He 一眼秒了这个斜优,,,,囧
分析不想写了,看代码吧 . (不过要除变乘??)
好像所有人的代码都是一样的????疑似 SSH .
参考代码
using namespace std;
typedef pair<int, int> pii;
typedef long long ll;
const int N = 5555;
int n, q[N], id[N];
ll a[N], dp[N][N], nINF;
int main()
{
file("d");
scanf("%d", &n);
for (int i=1; i<=n; i++) scanf("%lld", a+i), a[i] += a[i-1], id[i] = i;
stable_sort(id, id+1+n, [](const int& x, const int& y){return a[x] < a[y];});
memset(dp, -0x3f, sizeof dp); nINF = dp[0][0];
for (int i=0; i<=n; i++) dp[i][0] = 0;
for (int i, j=1; j<=n; j++)
{
int head = 1, tail = 0;
for (int _=0; _<=n; _++) // push
{
i = id[_];
if (i >= j) continue;
while ((head < tail) && (dp[j][q[tail - 1]] - dp[j][q[tail]]) * (a[q[tail]] - a[i]) <= (dp[j][q[tail]] - dp[j][i]) * (a[q[tail-1]] - a[q[tail]])) --tail;
q[++tail] = i;
}
for (int _=n, k; _>=0; _--) // pop
{
i = id[_];
if (i <= j) continue;
while ((head < tail) && (dp[j][q[head]] - dp[j][q[head + 1]] <= (a[i] - a[j]) * (a[q[head]] - a[q[head + 1]]))) ++head;
k = q[head];
chkmax(dp[i][j], dp[j][k] + (a[i] - a[j]) * (a[j] - a[k]));
}
}
ll ans = nINF;
for (int i=0; i<=n; i++) chkmax(ans, dp[n][i]);
printf("%lld\n", ans);
return 0;
}
Day 3
简单打了一下,跟 GOODBOUNCE 似的 .
A. Watching Fireworks is Fun
原题链接:CF372C .
hzoi 翻译的题面太怪了,读错题直接 GG,然而样例都是 \(d=1\) 也看不出来 >_<
首先什么烟花一起放都是诈骗的,只需要考虑前一个烟花到后一个走的位移即可 .
朴素 DP 还是非常显然的 .
令 \(dp_{i,j}\) 为处理到第 \(i\) 个烟花,目前在 \(j\) 的答案,于是:
其中 \(\Delta=t_i-t_{i-1}\) 是时间差 .
然后注意此时 DP 数组大小是 \(150000\times 300\times 64\text{ Byte}\approx 2746\text{ MiB}\) 的,显然是开不下的,必须要滚一维才行 .
然后这样暴力 DP 是 \(O(n^2m)\) 的,肯定过不去,考虑优化转移,首先看这个区间 \(\max\) 可以线段树或 ST 表优化,这样就是 \(O(nm\log n)\) 的,但是如果你写了就会发现这题点名卡带 \(\log\) 做法 XD .
首先忽略掉区间范围的 \(\min,\max\),就发现区间长度不变,加上 \(\min,\max\) 就只需要一些小的边界判断,单调队列优化即可做到 \(O(nm)\),可以通过此题 .
听说需要两次单调队列?不用吧 /kx
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 150514;
ll n, m, d, a[N], b[N], t[N], dp[N], nw[N], q[N];
int main()
{
file("fire");
scanf("%lld%lld%lld", &n, &m, &d);
for (int i=1; i<=m; i++) scanf("%lld%lld%lld", a+i, b+i, t+i);
for (int _=1; _<=m; _++)
{
int r = min((t[_] - t[_-1]) * d, n), head = 1, tail = 0;
for (int i=1; i<=r; i++)
{
while ((head <= tail) && (dp[q[tail]] < dp[i])) --tail;
q[++tail] = i;
}
for (int i=1; i<=n; i++)
{
if (i + r <= n)
{
while ((head <= tail) && (dp[q[tail]] < dp[i + r])) --tail;
q[++tail] = i + r;
}
while (q[head] < i - r) ++head;
nw[i] = dp[q[head]];
}
for (int i=1; i<=n; i++) dp[i] = nw[i] + b[_] - abs(a[_] - i);
}
ll ans = LLONG_MIN;
for (int i=1; i<=n; i++) chkmax(ans, dp[i]);
printf("%lld\n", ans);
return 0;
}
B. Perform巡回演出
简单 DP .
\(dp_{i,j}\) 表示到了第 \(i\) 个点,走了 \(j\) 天,每次暴力往外扩展即可,\(O(n^2k)\) .
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 11, K = 1111;
int n, kkk;
ll dp[N][K];
vector<int> w[N][N];
inline void solve()
{
for (int i=1; i<=n; i++)
for (int j=1, sz; j<=n; j++)
{
if (i == j) continue;
scanf("%d", &sz);
for (int k=0, x; k<sz; k++) scanf("%d", &x), w[i][j].emplace_back(x);
}
memset(dp, 0x7f, sizeof dp);
dp[1][0] = 0;
for (int k=0; k<kkk; k++)
for (int i=1; i<=n; i++)
for (int j=1; j<=n; j++)
{
if (i == j) continue;
int sz = w[i][j].size(), val = w[i][j][k % sz];
if (!val) continue;
chkmin(dp[j][k+1], dp[i][k] + val);
}
if (dp[n][kkk] == dp[0][0]) puts("0");
else printf("%lld\n", dp[n][kkk]);
}
int main()
{
file("perform");
while (~scanf("%d%d", &n, &kkk) && n && kkk) solve();
return 0;
}
C. 枪战Maf
原题链接:[POI2008] MAF-Mafia .
这题还是非常好想的,就是细节比较多 .
首先每个人和他的 Aim 连边变成一个基环内向森林 .
最小就是环留下一个,基环树或者树就全干掉 .
最大就拓扑一下,每次把入度为 0 的点的出点干掉,整环直接环长除以二 .
时间复杂度 \(O(n)\) .
参考代码
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 500;
vector<int> g[N];
int n, to[N], in[N], leaf[N], minn, maxn;
bool cycle[N], color[N];
inline void bfs()
{
queue<int> q;
for (int i=1; i<=n; i++)
if (!in[i]){q.push(i); ++minn; ++maxn;}
while (!q.empty())
{
int u = q.front(); q.pop();
cycle[u] = true; u = to[u];
if (color[u]) continue;
color[u] = 1;
int v = to[u]; leaf[v] = true;
if (!--in[v]) ++minn, q.push(v);
}
for (int i=1; i<=n; i++) cycle[i] ^= 1;
}
inline void dfs()
{
for (int i=1; i<=n; i++)
if (cycle[i] && !color[i])
{
int now = i, len = 0; bool cyc = false;
while (true)
{
if (!cycle[now]) break;
cycle[now] = false; cyc |= leaf[now];
now = to[now]; ++len;
}
if ((len > 1) && !cyc) ++maxn; // Warn. len = 1
minn += (len >> 1);
}
}
int main()
{
file("maf");
scanf("%d", &n);
for (int i=1; i<=n; i++){scanf("%d", to + i); ++in[to[i]];}
bfs(); dfs();
printf("%d %d\n", n - minn, n - maxn);
return 0;
}
D. 翻转游戏
原题链接:POJ1753 Filp Game .
爆搜,状态压缩一下,每次转移的时候展开即可 .
参考代码
代码比较丑 .
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 500;
bool vis[N];
int st, ans = -1, tmp[4][4];
inline int trans_to_num()
{
int x = 0;
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) x = (x << 1) + tmp[i][j];
return x;
}
inline void trans_to_arr(int x)
{
for (int i=3; i>=0; i--)
for (int j=3; j>=0; j--){tmp[i][j] = x & 1; x >>= 1;}
}
inline void bfs()
{
queue<pii> q; q.push(make_pair(st, 0));
while (!q.empty())
{
auto _ = q.front(); int x = _.first, step = _.second; q.pop();
if (vis[x]) continue;
vis[x] = true;
if (!x || (x == 65535)){printf("%d\n", step); exit(0);}
trans_to_arr(x);
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
{
tmp[i][j] ^= 1;
if (i-1 >= 0) tmp[i-1][j] ^= 1;
if (i+1 < 4) tmp[i+1][j] ^= 1;
if (j-1 >= 0) tmp[i][j-1] ^= 1;
if (j+1 < 4) tmp[i][j+1] ^= 1;
int nx = trans_to_num();
if (!vis[nx]) q.push(make_pair(nx, step + 1));
tmp[i][j] ^= 1;
if (i-1 >= 0) tmp[i-1][j] ^= 1;
if (i+1 < 4) tmp[i+1][j] ^= 1;
if (j-1 >= 0) tmp[i][j-1] ^= 1;
if (j+1 < 4) tmp[i][j+1] ^= 1;
}
}
}
string s[4];
int main()
{
file("flip");
for (int i=0; i<4; i++) cin >> s[i];
for (int i=0; i<4; i++)
for (int j=0; j<4; j++) st = (st << 1) + (s[i][j] == 'b');
bfs();
puts("Impossible");
return 0;
}
以下是博客签名,正文无关
本文来自博客园,作者:Jijidawang,转载请注明原文链接:https://www.cnblogs.com/CDOI-24374/p/16349744.html
版权声明:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议(CC BY-NC-SA 4.0)进行许可。看完如果觉得有用请点个赞吧 QwQ