2022.3.14
蓝书
AcWing 122. 糖果传递
和七夕祭那题一毛一样,就是环形均分纸牌问题。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e6+10,INF=1e8;
int a[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
ll sum = 0,ans=0;
for (int i = 1; i <= n;i++)
{
cin >> a[i];
sum += a[i];
}
sum /= n;
for (int i = 1; i <= n;i++)
{
a[i] -= sum;
a[i] += a[i - 1];
}
sort(a + 1, a + 1 + n);
for (int i = 1; i <= n;i++)
{
ans += abs(a[i] - a[n + 1 >> 1]);
}
cout << ans;
return 0;
}
AcWing 123. 士兵
中位数,分别求到达x和y轴的中位数,因为x的位置可能有重叠,但队伍最后的相对位置是不变,我们可以让每个数都减去他们是第几个数,这样并不改变相对位置。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e4+10,INF=1e8;
int x[N], y[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int n;
cin>>n;
for (int i = 1; i <= n;i++)
{
cin >> x[i] >> y[i];
}
sort(x + 1, x + 1 + n);
sort(y + 1, y + 1 + n);
for (int i = 1; i <= n;i++)
x[i] -= i;
sort(x + 1, x + 1 + n);
ll ans = 0;
for (int i = 1; i <= n;i++)
{
ans += abs(x[i] - x[n + 1 >> 1]);
ans += abs(y[i] - y[n + 1 >> 1]);
}
cout << ans;
return 0;
}
AcWing 124. 数的进制转换
把需要转换的进制读进来,利用短除法可以将一个a进制的数不断除以b,所得的余数最后从下往上拼起来就是b进制的数。一直各种原因wa,看了题解才勉强过
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=1e4+10,INF=1e8;
int x[N], y[N];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin>>t;
while(t--)
{
int a, b;
string aa, bb;
cin >> a >> b >> aa;
vector<int> cc,dd;
for(auto t:aa)
{
if(t>='0'&&t<='9')
cc.push_back(t - '0');
else if(t>='A'&&t<='Z')
cc.push_back(t - 'A' + 10);
else if(t<='z')
cc.push_back(t - 'a' + 36);
}
reverse(cc.begin(), cc.end());
while(cc.size())
{
int t = 0;
for (int i = cc.size()-1; i >=0;i--)
{
cc[i] += t * a;
t = cc[i] % b;
cc[i] /= b;
}
dd.push_back(t);
while(cc.size()&&cc.back()==0)
cc.pop_back();
}
reverse(dd.begin(), dd.end());
cout << a << ' ' << aa << '\n';
cout << b << ' ';
for(auto x:dd)
{
char c;
if (x>=0&&x <= 9) c= '0' + x;
if (x>=10&&x <= 35) c = 'A' + x - 10;
if(x>=36) c= 'a' + x - 36;
cout << c;
}
cout << '\n'<<'\n';
}
return 0;
}
AcWing 179. 八数码
思路:先判断输入有无解,根据之前写过的奇数码的题,当且仅当两个局面的逆序对的奇偶性相同时才有解,因此需要先判断。本题的A*函数F(x)=G+H,g为起点到当前状态的距离,以及当前状态到终点的距离,对于当前状态,我们需要求出当前状态到最终态的所有点和他们对应位置的曼哈顿距离之和,然后我们需要用哈希表保存下当前的每一个状态以及到起点到当前状态的距离。每次从堆里取出当前F(x)最小的状态,找到当前"x"的位置,并枚举"x"的上下左右四个方向并判断位置是否合法,如果合法则转移状态,注意此时会改变原来的状态(因为你还得用原来的状态去转移其他的状态)所以得先备份。之后需要更新一下当前的状态距离,并且需要记录下操作和之前的状态。如果我们最后转移到了终点那么就break,并输出方案。因为要求输出操作,我们还得用map存储每一个状态是由哪个状态转移过来的,最后再将操作步骤翻转输出。
关于unordered_map的使用看了yxc的代码,洗完澡重写。(已重写,一发过了)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<unordered_map>
using namespace std;
typedef long long ll;
typedef pair<int,string> pis;
typedef pair<int,int> pii;
unordered_map<string, int> dis;
unordered_map<string, pair<char,string> > last;
const int N=1e5+10,INF=1e8;
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
char op[5] = {'u', 'd', 'l', 'r'};
string st,ss,ed = "12345678x";
bool check(string a)
{
int cnt = 0;
for (int i = 0;i<8;i++)
for (int j = 0; j < i;j++)
if(a[i]<a[j])
cnt++;
return cnt & 1;
}
bool ok(int x,int y)
{
return (x >= 0 && x <= 2 && y >= 0 && y <= 2);
}
pii find(string a)
{
for (int i = 0; i < 9;i++)
{
if(a[i]=='x')
{
return {i / 3, i % 3};
}
}
}
int dist(string a)
{
int cnt = 0;
for (int i = 0; i < 9; i ++ )
{
if (a[i] != 'x')
{
int x = a[i] - '1';
cnt += abs(x / 3 - i / 3) + abs(x % 3 - i % 3);
}
}
return cnt;
}
string bfs(string st)
{
priority_queue<pis, vector<pis>, greater<pis> > que;
string ans;
dis[st] = 0;
que.push({dist(st), st});
//cout << dist(st) << endl;
while(que.size())
{
auto t = que.top();
que.pop();
string nowstate = t.second;
//cout << nowstate << endl;
string nextstate = nowstate;
if(nowstate==ed)
break;
auto tt = find(nowstate);
//cout << tt << endl;
int x = tt.first, y = tt.second;
//cout<<x<<' '<<y<<endl;
for (int i = 0; i < 4;i++)
{
int xx = x + dx[i], yy = y + dy[i];
if(ok(xx,yy))
{
nextstate = nowstate;
swap(nextstate[x * 3 + y], nextstate[xx * 3 + yy]);
if(dis.count(nextstate)==0||dis[nextstate]>dis[nowstate]+1)
{
dis[nextstate] = dis[nowstate] + 1;
last[nextstate] = {op[i], nowstate};
que.push({dist(nextstate) + dis[nextstate], nextstate});
}
}
}
}
while(ed!=st)
{
ans += last[ed].first;
ed = last[ed].second;
}
reverse(ans.begin(), ans.end());
return ans;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
for (int i = 0; i < 9;i++)
{
char c;
cin >> c;
getchar();
st += c;
if(c!='x')
ss += c;
}
if(check(ss))
{
cout << "unsolvable";
return 0;
}
string ans = bfs(st);
cout << ans << '\n';
return 0;
}
AcWing 180. 排书
IDA*相当于迭代加深的DFS,每次我们限定一个深度,并确定一个估价函数,如果当前状态加上估价函数已经超过限制深度的话就回溯,但找估价函数也难,本题每段数在换位置的时候至多会改变3个数的后继。由题意我们限定深度为4层,如果搜索超过4层就直接输出5及以上,如果cnt=0则说明排序成功,返回。对于每一层我们枚举区间i,j即哪一段该换以及该换的位置,备份数组a,如果成功则继续向下搜索。
但是难就难在如何把数组的某一段给换了,昨晚想了很久一直没做好,今早想到可以利用vector将数组的要换的地方放进去,其他地方保持不变,最后再覆盖掉原来的数组。估价函数用ceil的时候超时了,看了代码发现向上取整得用(cnt+2)/3。
发现不用vector跑的飞快90ms,用了vector快飞到600ms了。。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
const int N=10+10,INF=1e8;
int a[N],n;
int check()
{
int cnt = 0;
for (int i = 1; i < n;i++)
{
if(a[i+1]!=a[i]+1)
cnt++;
}
return cnt;
}
bool dfs(int dep,int tot)
{
if(((check()+2)/3)+tot>dep)
return 0;
if(!check())
return 1;
for (int i = 1; i<= n;i++)
{
for (int j = i; j <= n;j++)
{
for(int k=j+1;k<=n;k++)
{
int back[N];
vector<int> aa;
memcpy(back, a, sizeof a);
for (int u = j + 1; u <= k;u++)
aa.push_back(a[u]);
for (int u = i; u <= j; u++)
aa.push_back(a[u]);
int aasize = aa.size();
for (int u = 0,uu=i; u < aasize;u++,uu++)
a[uu] = aa[u];
/*只使用数组的做法
int x, y;
for (x = j + 1, y = i; x <= k; x ++, y ++ ) a[y] = back[x];
for (x = i; x <= j; x ++, y ++ ) a[y] = back[x];
*/
if (dfs(dep, tot + 1))
return 1;
memcpy(a, back, sizeof back);
}
}
}
return 0;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--)
{
cin>>n;
for (int i = 1; i <= n;i++)
cin >> a[i];
int dep;
for (dep = 0; dep <= 4;dep++)
{
if(dfs(dep, 0))
break;
}
if (dep == 5)
cout << "5 or more" << '\n';
else
cout << dep << '\n';
}
return 0;
}