2022.3.8
蓝书
AcWing 103. 电影
思路:将所有科学家讲的语言以及字幕和语音的语言存到一个数组里,然后对数组进行排序去重,然后开一个ans数组保存答案,先利用二分查找将科学家懂的语言保存在答案数组里,然后对于电影的字幕和语言语言分别二分找一下它们在ans数组里的个数,即懂得这个语言的科学家的数量,找到最优值,注意如果懂得语音的人一样多的话我们还得尽量让懂得该语言字幕的多一点。如果没有一个电影符合,那随便输出哪一个电影都行。
一开始wa了一次,以为是数组开小了,思考了一下发现有可能每个科学家,每个字幕和语音的语言都不一样,因此得开到3*N。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=2e5+10,INF=1e8;
int lang[3*N], a[N], b[N], c[N], ans[3*N], cnt;
int main()
{
int n,m;
scanf("%d", &n);
for (int i = 1; i <= n;i++)
{
scanf("%d", &a[i]);
lang[++cnt] = a[i];
}
scanf("%d", &m);
for (int i = 1; i <=m;i++)
{
scanf("%d", &b[i]);
lang[++cnt] = b[i];
}
for (int i = 1; i <= m;i++)
{
scanf("%d", &c[i]);
lang[++cnt] = c[i];
}
sort(lang + 1, lang + 1 + cnt);
int uni = unique(lang + 1, lang + 1 + cnt) - lang - 1;
cnt = uni;
for (int i = 1; i <= n;i++)
{
ans[lower_bound(lang + 1, lang + 1 + cnt, a[i]) - lang]++;
}
int ans0 = 0, ans1 = 0, ans2 = 0;
for (int i = 1; i <= m;i++)
{
int x = ans[lower_bound(lang + 1, lang + 1 + cnt, b[i]) - lang];
int y = ans[lower_bound(lang + 1, lang + 1 + cnt, c[i]) - lang];
if(x>ans1||x==ans1&&y>ans2)
{
ans0 = i, ans1 = x, ans2 = y;
}
}
if(!ans0)
printf("1\n");
else
printf("%d\n", ans0);
return 0;
}
AcWing 104. 货仓选址
思路:排序一下前缀和,利用中位数到所有点的距离之和最小的性质
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int n,a[N];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n;i++)
{
scanf("%d", &a[i]);
}
sort(a + 1, a + 1 + n);
ll sum = 0;
for (int i = 1; i <= n;i++)
{
sum += abs(a[i] - a[(n + 1) >> 1]);
}
printf("%lld", sum);
return 0;
}
AcWing 105. 七夕祭
思路:为了让每行和每列的小摊数量一样多,读入数据时我们先判断一下如果t不能整除n和m的话那就一定不可能满足条件了(毕竟不可能把小摊拆成几半捏)。然后我们分别处理一下同时满足n和m,只满足n,只满足m这三种情况,若满足n,为了让每一行的小摊数量都一样多,可以转化成均分分纸牌的,其中最少的操作次数就是每一个减去平均值的绝对值之和,为了操作方便我们可以让所有数都减去平均值,只要最后的前缀和都是0那就满足了条件,但是这里的每一行的左右边界在题目里是算相邻的,使得整个序列变成了环形,那么我们可以试一下枚举点k,也就是切开环的某一点使其转化为普通的均分纸牌,那么对于点k,根据公式abs|s[i]-s[k]|求和(1<=i<=n),当k是中位数的时候,此时求得的操作数量是最少的。
一开始排序的时候排错了,把行和列给排序了,后面想起其实要排的是前缀和数组,与行和列的排序无关。然后就是求每行或列的平均值的时候不用特意去求,因为你最终要满足平均值其实就是t/n和t/m。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=1e5+10,INF=1e8;
int n, m, t, mid,row[N], col[N], s[N];
ll ans;
int main()
{
scanf("%d%d%d", &n, &m, &t);
if((t%m!=0)&&(t%n!=0))
{
printf("impossible");
return 0;
}
for (int i = 1; i <= t;i++)
{
int x, y;
scanf("%d%d", &x, &y);
row[x]++, col[y]++;
}
if(t%n==0)
{
mid = t / n;
for (int i = 1; i <= n;i++)
{
row[i] -= mid;
s[i] = s[i - 1] + row[i];
}
sort(s+1,s+1+n);
for (int i = 1; i <= n;i++)
{
ans += abs(s[i] - s[(i + 1) >> 1]);
}
}
if(t%m==0)
{
mid = t / m;
for (int i = 1; i <= m;i++)
{
col[i] -= mid;
s[i] = s[i - 1] + col[i];
}
sort(s+1,s+1+m);
for (int i = 1; i <= m;i++)
{
ans += abs(s[i] - s[(i + 1) >> 1]);
}
}
if(t%m==0&&t%n==0)
printf("both ");
else if(t%n==0)
printf("row ");
else
printf("column ");
printf("%lld", ans);
return 0;
}
AcWing 175. 电路维修
思路:相当于一个边权为0或1的无向图,链式前向星存图,然后求bfs加deque求最短路,按照蓝书的思路,如果这个点已经访问过了那就不用管了,然后更新一下最小值。
一开始写了bfs的代码,但是发现如何保存每一个点的坐标很麻烦想到用pair和map,但是搞了半天显示语法有点问题并且还要判断4个走的方向和4个你要转移的格子的方向,于是调了半天后换一种了思路写。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
#include<deque>
using namespace std;
typedef long long ll;
typedef pair<int,int> PII;
const int N=500+10,INF=1e8;
int dis[N*N],vis[N*N],head[N*N],n,m,t,cnt;
struct node
{
int to,ne,w;
} edge[N*N*5];
void add(int a,int b,int w)
{
edge[++cnt].to = b;
edge[cnt].w = w;
edge[cnt].ne = head[a];
head[a] = cnt;
}
void bfs()
{
for (int i = 0; i<N*N;i++)
{
dis[i] = INF;
vis[i] = 0;
}
deque<int> que;
que.push_front(0);
dis[0] = 0;
while(que.size())
{
int t = que.front();
que.pop_front();
if(vis[t])
continue;
vis[t] = 1;
for (int i = head[t];i;i=edge[i].ne)
{
if(dis[edge[i].to]>dis[t]+edge[i].w)
{
dis[edge[i].to] = dis[t] + edge[i].w;
if(edge[i].w==1)
que.push_back(edge[i].to);
else
que.push_front(edge[i].to);
}
}
}
}
int main()
{
scanf("%d", &t);
while(t--)
{
memset(head, 0, sizeof head);
cnt = 0;
scanf("%d%d", &n, &m);
for (int i = 0; i < n;i++)
for (int j = 0; j <m;j++)
{
char s;
cin >> s;
int a = (m + 1) * i + j, b = (m + 1) * (i + 1) + j+1;
if((s=='\\'))
{
add(a , b, 0);
add(b , a, 0);
add(a + 1, b - 1, 1);
add(b - 1, a + 1, 1);
}
else
{
add(a + 1, b - 1 ,0);
add(b - 1, a + 1, 0);
add(a , b , 1);
add(b , a , 1);
}
}
bfs();
if(dis[n*(m+1)+m]!=INF)
printf("%d\n", dis[n*(m+1)+m]);
else
printf("NO SOLUTION\n");
}
return 0;
}
AcWing 176. 装满的油箱
一开始本想直接用dijkstra求最短路的,但还是按照蓝书的优先队列bfs写了一下,其中遇到了几个问题,不知道优先队列如何存三个变量,查了一下,然后调代码的时候变量名太多容易打错变量,调完交了一下,wa了3发,2发超内存,1发超时,超内存看了一下发现根本不用开这么大,油箱最多也就100,改完再交,超时。发现遍历城市的时候遍历太多次了,于是临时加了个city数组记录一下每个城市能到的下标最大的城市,然后过了,但是其实直接用链式前向星存图然后再解就好了根本没必要这么麻烦,还得优化优化。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N=1e4+10,INF=1e8;
int n,m,q,c,s,e,w[N][N],vis[N][105],d[N][105]oil[N],city[N];
struct node
{
int now,f,dis;
node(int x,int y,int z):now(x),f(y),dis(z) {}
friend bool operator < (node a,node b)
{
return a.dis>b.dis;
}
};
int bfs()
{
priority_queue<node> que;
que.push(node(s,0,0));
d[s][0] = 0;
while(que.size())
{
node t = que.top();
que.pop();
//printf("%d %d %d\n", t.dis, t.now, t.c);
int x = t.now, y = t.f;
if(x==e)
return t.dis;
if(vis[x][y])
continue;
vis[x][y] = 1;
if(y<c&&d[x][y+1]>d[x][y]+oil[x])
{
d[x][y + 1] = d[x][y] + oil[x];
que.push(node(x, y + 1,d[x][y+1]));
}
for (int i = 0; i <= city[x];i++)
{
//printf("%d %d %d\n", x, i,w[x][i]);
if(w[x][i]==0) continue;
if(w[x][i]<=y&&d[i][y-w[x][i]]>t.dis)
{
d[i][y-w[x][i]] =t.dis;
que.push(node(i, y - w[x][i],t.dis));
}
}
}
return 0;
}
int main()
{
scanf("%d%d", &n, &m);
for (int i = 0; i < n;i++)
{
scanf("%d", &oil[i]);
}
for (int i = 1; i <= m;i++)
{
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
w[a][b] = w[b][a] = c;
city[a] = max(city[a], b);
city[b] = max(city[b], a);
}
scanf("%d", &q);
while(q--)
{
memset(vis, 0, sizeof vis);
memset(d,0x3f, sizeof d);
scanf("%d%d%d", &c, &s, &e);
int ans = bfs();
if(ans)
printf("%d\n", ans);
else
printf("impossible\n");
}
return 0;
}