(该亏的钱咱是一分不省T^T
另外只会1346哦 o.0
目录
1.最小花费
n个3维坐标,求把它们变成其中两个一样(xy,yz,xz),另一个 i,i+1 i+2......的最小花费
思路:模拟(将所有点移动到一点,到中间点的距离即为最小花费,等差数列直接就近移)
//zjx6666
#include<iostream>
#include<algorithm>
#include<cmath>
using namespace std;
const int maxn=10000010;
int a[maxn];
int b[maxn];
int c[maxn];
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
cin>>a[i]>>b[i]>>c[i];
sort(a+1,a+n+1);//三个坐标间互不干扰,直接用三个数组维护
sort(b+1,b+n+1);
sort(c+1,c+n+1);
long long ans1=0,ans2=0,ans3=0;
int s=(n+1)/2;
for(int i=1;i<=n;i++)
{
ans1=ans1+abs(a[i]-a[s])+abs(b[i]-b[s])+abs(c[i]-(c[s]-s+i));//我懒得想就直接按题意模拟了
ans2=ans2+abs(a[i]-a[s])+abs(c[i]-c[s])+abs(b[i]-(b[s]-s+i));
ans3=ans3+abs(b[i]-b[s])+abs(c[i]-c[s])+abs(a[i]-(a[s]-s+i));
}
long long ans=min(ans1,ans2);
ans=min(ans2,ans3);
cout<<ans;//三种情况取个最小
return 0;
}
3.跳格子
跳格子,可向前1向后1或者瞬移到前面第一个值相同的(花费均为1)问到n的最小花费
思路:无权边最短路,直接无脑建边BFS
//zjx6666
#include<iostream>
#include<map>
#include<vector>
#include<queue>
using namespace std;
const int maxn=200100;
int a[maxn];
map<int,int>mp;
queue<int>q;
bool v[maxn];
int d[maxn];
vector<int>e[maxn];
int bfs(int x,int y)//无权最短路,简单的bfs(迪杰斯特拉也可,稍微麻烦一点)
{
q.push(x);
v[x]=1;
d[x]=0;
while(q.size())
{
int z=q.front();
q.pop();
for(int i=0;i<e[z].size();i++)
{
if(!v[e[z][i]])
{
if(e[z][i]==y)return (d[z]+1);
v[e[z][i]]=1;
d[e[z][i]]=d[z]+1;
q.push(e[z][i]);
}
}
}
}
int main()
{
int n;
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(!mp.count(a[i]))
{
mp[a[i]]=i;
}
else
{
e[mp[a[i]]].push_back(i);//mp中存的上一个与它值相等的点的编号
mp[a[i]]=i;//记得更新mp
}
if(i>1)e[i].push_back(i-1);
if(i<n)e[i].push_back(i+1);//与其前后点建边
}
cout<<bfs(1,n);
return 0;
}
2024.4.24增加人类智慧dp版本做法
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define fast ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
void solve();
using pii = pair<int, int>;
using db = double;
signed main(){
fast
int t = 1;
// cin >> t;
for(int i = 1; i <= t; i++){
solve();
}
}
const int N = 3e5 + 10, M = N * 2, P = 998244353;
int n, a[N];
int f[N], pos[N];
void solve(){
cin >> n;
int id=0;
for(int i = 1; i <= n; i++) cin >> a[i];
memset(f, 0x3f, sizeof f);
memset(pos, -1, sizeof pos);
f[0] = -1;
for(int i = 1; i <= n; i++){
int mn = f[i - 1] + 1;
if(pos[a[i]] != -1) mn = min(mn, f[pos[a[i]]] + 1);
f[i] = mn;
if(i > 1 && f[i] < f[i - 1] - 1){
for(int j = i - 1; j >= 1&&f[j]>mn+i-j; j--){
f[j] = min(f[j], mn + i - j);
id++;
}
}
pos[a[i]] = i;
}
cout << f[n];
}
4.鸡兔同笼加强版
鸡兔同笼3人版,n头m脚三种动物abc个脚,问c个脚的最少几只最多几只
思路:发现n只有1e4,果断枚举两个判另外一个是否符合条件(这场题好像比第一场水一点)
//zjx6666
#include<iostream>
using namespace std;
int main()
{
long long n,m,a,b,c,v=0;
long long ans1=n,ans2=0;
cin>>n>>m>>a>>b>>c;
for(long long i=0;i<=n;i++)
{
for(long long j=0;j<=n-i;j++)
{
long long s=m-(i*a+(j*b));
if(s>=0&&s%c==0&&i+j+(s/c)==n)//注意判下大于0,不幸在此哇一发
{
v=1;
ans1=min(s/c,ans1);
ans2=max(s/c,ans2);
}
}
}
if(v==0)cout<<-1;
else cout<<ans1<<" "<<ans2;
return 0;
}
6.追及问题
n个人每人一个初始位置x一个速度v,两人跑到同一位置时速度都变为小的那个(一起走,变为一组),问最多的一组几个人
思路:首先简单推一下,当xi<=xj并vi>vj时j一定会被i追上
首先考虑速度最小的一个点 i 一定会在某时刻被他前面的所有点追上(因为 i 速度最小),而他永远也追不上他后面的,由于 i 前面的点在与他相遇后速度合并为最小的v,所以也追不上 i 后面的点,这样所有位置在 i 之前的点就能组成一个集合,然后 i 后面的同理操作,找没有被 i 合并的下一个最小值,组成新的集合,取最大的就是答案
这样就有思路啦,我一开始n^2实现tle了(还是不能偷懒)老老实实排两次序吧T-T
comp1是为了提前算出对于每个编号为id的点,他的前面有几个点,这样可以在查找时避免循环的嵌套。然后第二次排序用v排(即速度小的在前),这样可以保证速度最小的先合并完(这样出来的集合是完整的)
//zjx6666
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
int n;
const int maxn=100010;
map<int,int>mp;
struct A{int x,v,id;};
A e[maxn];
int comp1(A p,A q)
{
if(p.x==q.x)return p.v>q.v;//注意x相等速度小的合并速度大的
return p.x<q.x;
}
int comp(A p,A q)
{
if(p.v==q.v)return p.x<q.x;//注意v相等前面的合不了后面的(平行)
return p.v<q.v;//这样保证了位置小的先合,方便维护
}
int main()
{
ios_base::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>e[i].x>>e[i].v;
e[i].id=i;//记录一下编号
}
sort(e+1,e+n+1,comp1);
for(int i=1;i<=n;i++)
mp[e[i].id]=i;//第一次排序为了记录第i个点前面有几个点(包括自己)方便o(n)实现
sort(e+1,e+n+1,comp);
int ans=0,l=0,s=0;//l为上一次合到的位置,s是之前合了几个
for(int i=1;i<=n;i++)//第二次排序完后速度小的在前,一定可以合并x在自己之前并且没被合并的
{
if(e[i].x>l)
{
ans=max(ans,mp[e[i].id]-s);
l=e[i].x;
s=mp[e[i].id];
}
}
cout<<ans;
return 0;
}