牛客小白月赛22
操作序列
思路
这题我觉得用map模拟其实很好,但是如果用线段树的话也还行,主要是线段树不是很会。
为了避免不必要的开销,map使用find函数,打印的时候用map.begin()->second就可以了啦。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main()
{
map<ll,ll> mp;
int n;
scanf("%d",&n);
char ch;
ll num1,num2;
while (n--) {
scanf("%lld%c",&num1,&ch);
if (ch==' ') {
scanf("%lld",&num2);
bool flag=true;
for (int i=num1-30;i<=num1+30;i++) {
if (mp.find(i)!=mp.end()) {
flag=false;
break;
}
}
if (flag) {
mp[num1]=num2;
}
}
else {
if (num1==-1) {
if (mp.begin()!=mp.end()) {
printf("%lld\n",mp.begin()->second);
mp.erase(mp.begin());
}
else {
printf("skipped\n");
}
}
else {
if (mp.find(num1)==mp.end()) {
printf("0\n");
}
else {
printf("%lld\n",mp[num1]);
}
}
}
}
return 0;
}
树上子链
思路
这题他们说是树形dp模板题,定义dp[i]是以i为根节点,不同时携带左右子树的值最大链。
这样对于每一个根节点就可以判断加上左右子树之后的最大值了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
vector<int> g[maxn];
int w[maxn];
ll dp[maxn],ans=-(1<<30);
void dfs(int u,int f) {
dp[u]=w[u];
for (auto v:g[u]) {
if (v==f) {
continue;
}
dfs(v,u);
ans=max(ans,dp[u]+dp[v]);
dp[u]=max(dp[u],dp[v]+w[u]);
}
ans=max(ans,dp[u]);
}
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<=n;i++) {
scanf("%d",&w[i]);
}
int u,v;
for (int i=0;i<n-1;i++) {
scanf("%d%d",&u,&v);
g[u].push_back(v);
g[v].push_back(u);
}
dfs(1,0);
printf("%lld\n",ans);
return 0;
}
货物种类
思路
这题就是暴力啦,所有点做排列,10!是3e6左右,10组问题不大。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int x[15],y[15],a[15];
inline int getDistance(int i,int j) {
return abs(x[i]-x[j])+abs(y[i]-y[j]);
}
int main()
{
int t;
scanf("%d",&t);
while (t--) {
int sx,sy;
scanf("%d%d",&sx,&sy);
scanf("%d%d",&sx,&sy);
int n;
scanf("%d",&n);
for (int i=0;i<n;i++) {
scanf("%d%d",&x[i],&y[i]);
a[i]=i;
}
a[n]=n;
x[n]=sx;
y[n]=sy;
int ans=1<<30;
do {
int c=getDistance(a[0],n)+getDistance(a[n-1],n);
for (int i=0;i<n-1;i++) {
// printf("%d %d %d ",a[i],a[i+1],getDistance(a[i],a[i+1]));
c+=getDistance(a[i],a[i+1]);
}
// printf("\n%d\n",c);
ans=min(ans,c);
} while (next_permutation(a,a+n));
printf("The shortest path has length %d\n",ans);
}
return 0;
}
仓库选址
思路
这题暴力都能过,没错就是你想的暴力,n^4,但是这题正解应该包括有前缀和和数学两种解法。
前缀和就很简单啦。
先求出来第一列,所有行的答案,然后递推其他点的答案。
关于递推,假设当前点是(x,y) ,下一个点是(x,y+1),则矩阵[m,x] (右上角的点,左下角(1,1))中的每个点到(x,y+1)的x距离都+1,但是y距离不变;矩阵[x+1,1]到[m,n]中的每个点x的距离都减一。
当我们知道(x,y)的答案之后,左边的矩阵总共需要走几次就加几,右边的矩阵需要走几次就减几就可以啦。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=105;
ll a[maxn][maxn],sum[maxn][maxn],cnt[maxn][maxn],c[maxn][maxn];
int main()
{
ll t;
scanf("%lld",&t);
while (t--) {
ll n,m;
scanf("%lld%lld",&n,&m);
for (ll i=1;i<=m;i++) {
for (ll j=1;j<=n;j++) {
scanf("%lld",&a[i][j]);
}
}
for (ll i=1;i<=m;i++) {
for (ll j=1;j<=n;j++) {
cnt[i][j]=cnt[i][j-1]+cnt[i-1][j]-cnt[i-1][j-1]+a[i][j];
a[i][j]=a[i][j]*(abs(i-1)+abs(j-1));
sum[i][j]=sum[i][j-1]+sum[i-1][j]+a[i][j]-sum[i-1][j-1];
}
}
c[1][1]=sum[m][n];
ll ans=min((ll)(1<<30),c[1][1]);
for (ll i=2;i<=m;i++) {
c[i][1]=c[i-1][1]-(cnt[m][n]-cnt[i-1][n])+cnt[i-1][n];
ans=min(c[i][1],ans);
}
for (ll i=1;i<=m;i++) {
for (ll j=2;j<=n;j++) {
c[i][j]=c[i][j-1]-(cnt[m][n]-cnt[m][j-1])+cnt[m][j-1];
ans=min(c[i][j],ans);
}
}
printf("%lld\n",ans);
}
return 0;
}