A 机器人
这个题分成两大种情况。
第一种情况: 要经过的仓库只在A 区。
先找到编号最小和编号最大的仓库
- 如果只要经过一个仓库,而且这个仓库就是起点 输出 0.
- 如果起点在编号最小的仓库。 答案就是 (编号最大的转折点 - 起点编号)* 2;
- 如果起点在编号最大的仓库。 答案就是 (起点编号 - 编号最小的转折点)* 2;
- 如果起点在中间, 答案就是 (编号最大的转折点 - 编号最小的转折点) * 2;
第二种情况: 要经过的仓库 A 区 B 区都有
- 如果起点在编号最小的仓库, 如果在 起点 和 B 区编号最小的仓库之间有转折点,答案就是 (编号最大的转折点 - 起点编号 + 费用) * 2, 反之答案就是 (编号最大的转折点 - 编号最小的转折点 + 费用) * 2;
- 如果起点在编号最大的仓库,同上。
- 如果起点在中间。(编号最大的转折点 - 编号最小的转折点 + 费用) * 2;
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5+1000;
int n,m,r,k,s;
int maxa,maxb,mina,minb,f[200],ans,xx,yy;
int main(){
int x,y;
scanf("%d%d%d%d%d",&n,&r,&m,&k,&s);
maxb = 0;
minb = n+1;
maxa = s; // 这个地方有没有考虑,起点肯定是必须经过的点啊。
mina = s;
for (int i = 0; i < r; ++i){
scanf("%d%d",&x,&y);
maxa = max(maxa,x); //最小编号的仓库,和最大编号的仓库。
mina = min(mina,x);
if (y == 1){
maxb = max(maxb,x); // 找的是 b 区中,编号最小和最大的仓库。
minb = min(minb,x);
}
}
for (int i = 1; i <= m; ++i)
scanf("%d",&f[i]);
f[0] = 1; f[++m] = n;
xx = 1; yy = n;
for (int i = 0; i <= m; ++i){
if (f[i] <= mina) xx = max(xx,f[i]); //找可以包含所有仓库的最小和最大转折点。
if (f[i] >= maxa) yy = min(yy,f[i]);
}
if (maxb == 0){ //不需要经过b区的仓库。
if (mina == maxa){ // 重写之后,这个地方没有考虑。有可能只要经过一个点。还只是起点。
} else
if (maxa == s){ // 起点是编号最大的仓库。
ans = 2*(s - xx);
} else
if (mina == s){ //起点是编号最小的仓库。
ans = 2 * (yy - s);
} else{ //起点在中间。
ans = (yy - xx) * 2;
}
} else{ //经过B 区的仓库。
if (maxa == s){ //类似。
for (int i = 0; i <= m; ++i){
if (f[i] <= s && f[i] >= maxb){
ans = 2 * (s - xx + k);
printf("%d\n",ans);
return 0;
}
}
} else
if (mina == s){
for (int i = 0; i <= m; ++i){
if (f[i] >= s && f[i] <= minb){
ans = 2 * (yy - s + k);
printf("%d\n",ans);
return 0;
}
}
}
ans = (yy - xx + k) * 2;
}
printf("%d\n",ans);
return 0;
}
B 吃豆豆
f[i][j][k] 代表 k 时间,走到了 i,j,吃到了多少豆豆。
i j 可以从上下左右和本身推导过来。
我写的时候,是从当前点推导到其余的五个点。
一开始 f 全部赋值为 -1, 起点为 0.
#include<bits/stdc++.h>
using namespace std;
const int N = 20000;
int f[15][15][N],t[16][16];
int dx[5] = {-1,1,0,0,0};
int dy[5] = {0,0,-1,1,0};
int xs,ys,xt,yt,n,m,K;
int main(){
scanf("%d%d%d",&n,&m,&K);
memset(f,-1,sizeof(f));
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
scanf("%d",&t[i][j]);
scanf("%d%d%d%d",&xs,&ys,&xt,&yt);
int tx,ty;
f[xs][ys][0] = 0;
for (int k = 1; k <= N; ++k){
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= m; ++j)
if (f[i][j][k-1] != -1){
for (int it = 0; it < 5; ++it){
tx = i + dx[it]; ty = j + dy[it];
if (t[tx][ty] != 0 && (k % t[tx][ty] == 0)){
f[tx][ty][k] = max(f[tx][ty][k],f[i][j][k-1] + 1);
} else {
f[tx][ty][k] = max(f[i][j][k-1],f[tx][ty][k]);
}
}
}
if (f[xt][yt][k] >= K){
printf("%d\n",k);
return 0;
}
}
return 0;
}
C 拆拆拆数
求读入的两个数是不是互质的,如果是就直接输出来。
不然。
暴力枚举前5个数,就一定会有答案出来。我也不知道为什么。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
long long gcd(long long a, long long b){
if(b== 0) return a; else return gcd(b,a%b);
}
int main(){
int T;
LL n,m;
scanf("%d",&T);
while(T--){
scanf("%lld%lld",&n,&m);
if (gcd(n,m)==1){
puts("1");
printf("%lld %lld\n",n,m);
} else{
bool vis = 0;
for (LL i = 2; i<= 5; ++i){
for (LL j = 2; j <= 5; ++j)
if (gcd(i,j)==1 && gcd(n-i,m-j) == 1){
vis = 1;
puts("2");
printf("%lld %lld\n",i,j);
printf("%lld %lld\n",n-i,m-j);
break;
}
if (vis) break;
}
}
}
return 0;
}
E 流流流动
这个是一个树形DP 的题。
我们把所有点连起来之后。成为的不是一棵树。而是多棵树。对树做DP 即可。
// f[x] 代表我选了 x 的子树最大值。 g[x] 代表我不选 x 的子树最大值。
// 更新f g 的时候,用到了f g 的子树。
#include<bits/stdc++.h>
using namespace std;
const int N = 2000;
vector<int>p[N];
int n,m,ans,a[N],b[N],f[N],g[N];
bool vis[N];
void dfs(int x, int fa){
f[x] = a[x]; g[x] = 0; vis[x] = 1;
int psize = p[x].size();
for (int i = 0; i < psize; ++i){
int v = p[x][i];
if (v == fa) continue;
dfs(v,x);
f[x] += max(f[v]-b[min(x,v)],g[v]);
g[x] += max(f[v],g[v]);
}
}
int main(){
scanf("%d",&n);
for (int i = 1; i <= n; ++i) scanf("%d",&a[i]);
for (int i = 1; i <= n; ++i) scanf("%d",&b[i]);
for (int i = 2; i <= n; ++i){
if (i & 1){
if (3*i+1 <= n){
p[i].push_back(3*i+1);
p[3*i+1].push_back(i);
}
} else{
p[i].push_back(i/2);
p[i/2].push_back(i);
}
}
int ans;
for (int i = 1; i <= n; ++i)
if (!vis[i]){
dfs(i,0);
ans += max(g[i],f[i]);
}
printf("%d\n",ans);
return 0;
}
F 爬爬爬山
直接按照 dij 算法来就可以了
如果遇到有高的山, 再加上另外一个权值。, (h[i] - h[1] - k)* (h[i] - h[1] - k)
这个题注意 long long
#include<bits/stdc++.h>
using namespace std;
typedef pair<long long,int> P; //
const int N = 2e5+100;
int a[N],n,m,k,t;
int Head[N],Next[N*2],To[N*2],cnt = 0,val[N*2];
long long dis[N];
bool vis[N];
priority_queue<P,vector<P>, greater<P> > q; //小根堆。
void add(int u, int v, int w){
++cnt;
Next[cnt] = Head[u];
To[cnt] = v;
val[cnt] = w;
Head[u] = cnt;
}
void dij(){
for (int i = 0; i <= n; ++i) dis[i] = 1e13;
dis[1] = 0; q.push(P(0,1));
while(!q.empty()){
int u = q.top().second; //找到dis 最小的起点。
q.pop();
if (vis[u]) continue;
vis[u] = 1; //这个起点已经更新过了。
for (int i = Head[u]; i; i = Next[i]){ // 更新之后的点。
int v = To[i];
long long w = val[i];
if (a[v] > t) w += 1ll*(a[v]-t)*(a[v]-t); //这个是另外加的权值。
if (dis[v] > dis[u] + w){
dis[v] = dis[u] + w;
q.push(P(dis[v],v));
}
}
}
return;
}
int main(){
int x,y,z;
scanf("%d%d%d",&n,&m,&k);
for (int i = 1; i <= n; ++i) scanf("%d",&a[i]);
for (int i = 0; i < m; ++i){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z); add(y,x,z);
}
t = a[1] + k; //判断能够达到最高的山高。
dij();
printf("%lld\n",dis[n]);
return 0;
}
I 起起落落
f[i] 代表以 i 结尾的序列有多少个。
然后我们两重循环暴力。
开一个变量 j 从 i - 1 向前循环,如果 a[j] < a[i] 那么 k ++,
否则就是 a[j] > a[i] , 那这样,, a[j] (中间的 k 个数) a[i] 就 又可以构成一个新的序列。
可以得到 f[i] += ( f[j] + 1 ) * k;
答案累加就可以了。
#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9+7;
int ans,n,m,k,a[2300];
long long f[2300];
int main(){
scanf("%d",&n);
for (int i = 1; i <= n; ++i)
scanf("%d",&a[i]);
for (int i = 3; i <= n; ++i){
k = 0;
for (int j = i-1; j > 0; j--)
if (a[j] < a[i]) k++; else{
f[i] = (f[i] + (1ll*(f[j]+1) * k)%mod) % mod;
}
ans = (ans + f[i]) % mod;
}
printf("%d\n",ans);
return 0;
}
J 夺宝奇兵
暴力枚举要的宝箱数 k ,每个人,如果有多于 k 个宝箱,就买下来那些多于 k 的宝箱,当然是从小到大买。
如果我还不够 k 个宝箱。那就从剩下的宝箱中 从小到大买。直到等于k 个宝箱。
#include<bits/stdc++.h>
using namespace std;
const int N = 2000;
typedef pair<int,int> P;
vector<P>g,f[N]; //g 总的宝箱。 f 每个人的宝箱、
bool vis[N];
long long ans,sum;
int n,m;
void init(){
int x,y;
scanf("%d%d",&n,&m);
for (int i = 0; i < m; ++i){
scanf("%d%d",&x,&y);
g.push_back(P(x,i));
f[y].push_back(P(x,i));
}
sort(g.begin(),g.end());
for (int i = 1; i <= m; ++i)
sort(f[i].begin(),f[i].end());
}
void solve(int k){
int tt,t= 0;
sum = 0;
for (int i = 1; i <= m; ++i){
tt = f[i].size() - k;
for (int j = 0; j < tt; ++j){
sum += f[i][j].first;
vis[f[i][j].second] = 1;
t++;
}
}
tt = g.size();
for (int i = 0; i < tt; ++i)
if (vis[g[i].second] == 0){
if (t <= k) t++; else break;
sum += g[i].first;
}
ans = min(ans,sum);
}
int main(){
init();
ans = 1e13;
for (int i = m; i > 0; i--){
for (int j = 0; j <= m; ++j)
vis[j] = 0;
solve(i);
}
printf("%lld\n",ans);
return 0;
}