A题 younik进入医院(并查集)
简化题意
有n个人,有m对好朋友关系(1<=n,m<=1e6),朋友关系不传递,这n个人要去医院,如果当前要进去的人医院里面没有他的朋友他就会难过,让我们安排一个进入医院的顺序,使得在难过人数最小的情况下序列字典序也最小;输出最少难过的人数和安排的序列;
题解
首先不难想到,连通块的个数就是难过的人数;其次在思考如何保证字典序最小,不难想到用小根堆的优先队列,让每个连通块中最小的编号先入队,接着让他没有被访问过的邻接点入队即可;
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 1e6 + 55;
int n,m;
int p[MAXN],vis[MAXN];///p[i]记录i点父亲是谁,vis[i]记录第i个人是否被访问过;
/** 路径压缩并查集 */
inline int Fa(int x)
{
if(x != p[x])
{
int fa = p[x];
p[x] = Fa(p[x]);
}
return p[x];
}
inline void mix(int u,int v)
{
int fu = Fa(u),fv = Fa(v);
if(fu != fv)p[fu] = fv;
}
set<int>st[MAXN];///用set寻找每个连通块中最小编号;
vector<int>e[MAXN],res;///e数组建边,res记录答案;
inline void solve()
{
priority_queue<int,vector<int>,greater<int> >pq;
for(int i = 1;i <= n;i ++)
{
if(st[i].size() > 0)
{
int v = *st[i].begin();
pq.push(v);
vis[v] = 1;
}
}
while(!pq.empty())
{
int u = pq.top();pq.pop();
res.push_back(u);
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i];
if(vis[v] == 0)
{
vis[v] = 1;
pq.push(v);
}
}
}
}
int main()
{
int q;scanf("%d",&q);
while(q --)
{
int ans = 0;
scanf("%d%d",&n,&m);
res.clear();
for(int i = 1;i <= n;i ++)p[i] = i,e[i].clear(),st[i].clear(),vis[i] = 0;
for(int i = 1;i <= m;i ++)
{
int u,v;scanf("%d%d",&u,&v);
mix(u,v);
e[u].push_back(v);
e[v].push_back(u);
}
///这步很重要,因为路径压缩时不会让所有节点都只想"根节点",for一遍强行让在同一连通块的点指向"根节点",为下一步做准备;
for(int i = 1;i <= n;i ++)if(p[i] != p[p[i]])p[i] = p[p[i]];
for(int i = 1;i <= n;i ++)
{
if(p[i] == i)ans ++;///发现一个连通块记录答案;
st[p[i]].insert(i);///将同一连通块的节点放到一起,最前面的点就是该连通块中编号最小点;
}
solve();
printf("%d\n",ans);
for(int i = 0;i < res.size();i ++)printf("%d ",res[i]);
putchar(10);
}
}
B题 病毒被消灭(bfs)(签到题)
简化题意
给出n*n的矩阵,矩阵值正数表示危险度,0则表示危险度无限大(不能走),现在要从{1,1}走到{n,n},求出路径危险度总和最小,如果不能走到则输出0;
题解
根据题意直接bfs即可,首先判断该点是否合法(不超出边界和该点值不为0),松弛一下危险度取最小即可;
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
const ll LINF = 1e16;
int n;
ll mp[105][105];
ll dp[105][105];
int dx[] = {0,0,1,-1};
int dy[] = {1,-1,0,0};
void bfs()
{
queue<int>pq;
pq.push(1),pq.push(1);
dp[1][1] = mp[1][1];
while(!pq.empty())
{
int x = pq.front();pq.pop();
int y = pq.front();pq.pop();
for(int i = 0;i < 4;i ++)
{
int fx = x + dx[i];
int fy = y + dy[i];
if(fx >= 1 && fx <= n && fy >= 1 && fy <= n && mp[fx][fy] != 0)
{
if(dp[fx][fy] > dp[x][y] + mp[fx][fy])
{
dp[fx][fy] = dp[x][y] + mp[fx][fy];
pq.push(fx);
pq.push(fy);
}
}
}
}
}
int main()
{
scanf("%d",&n);
for(int i = 1;i <= n;i ++)for(int j = 1;j <= n;j ++)dp[i][j] = LINF,scanf("%lld",&mp[i][j]);
bfs();
if(dp[n][n] >= LINF)printf("0");
else printf("%lld",dp[n][n]);
}
C题 younik要挂号
本蒟蒻不知,待更新~
D题 younik要排号(简单模拟)(签到题)
啊这就是简单模拟,没什么好说的好像;
相信大家都会的~
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
int n;
string s;
set<string>st;
int main()
{
scanf("%d",&n);
int ans = 0;
for(int i = 1;i <= n;i ++)
{
cin >> s;
st.insert(s);
if(s == "younik" && ans == 0)ans = st.size();
}
printf("%d\n",ans);
}
E题 younik要吃午饭啦(贪心)
简化题意
在二维平面上有一些点,现在在能在x轴上安放武器,武器能覆盖半径为r的区域,问最小要用多少个武器覆盖所有的点,若不能全部覆盖则输出-1;
题解
首先判断-1的情况,不难得出纵坐标到达x轴的距离大于R时即为-1;
然后,先计算出每个点能被覆盖时圆心的位置范围,用pair存储左右端点,存入到vector中,sort整个vector(即按照左边升序排序,如果左端点一致则按照右端点升序排序),贪心选择区间右端点,若下一个区间的左端点大于当前的右端点说明需要新开一个武器,然后再令当前的右端点等于当前武器的右端点;遍历一次即可;
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int MAXN =1e5 + 59;
const int INF = 0x3f3f3f3f;
int n,R;
pair<double,double>pr;
vector<pair<double,double> >e;
int main()
{
while(scanf("%d%d",&n,&R) + 1)
{
int flag = 1;
e.clear();
if(n == 0 && R == 0)break;
for(int i = 1;i <= n;i ++)
{
double x,y;scanf("%lf%lf",&x,&y);
if(y < 0)y = -y;
if(y > R)flag = 0;
double len = sqrt(R * R - y * y);
pr = make_pair(x - len,x + len);
e.push_back(pr);
}
sort(e.begin(),e.end());
if(flag == 0)printf("-1\n");
else
{
int ans = 0;
double r = -INF;
for(int i = 0;i < e.size();i ++)
{
if(e[i].first <= r)continue;
else
{
ans ++;
r = max(r,e[i].second);
}
}
printf("%d\n",ans);
}
}
}
F题 新冠病毒要回家(次短路)
题意
这题实在太明显啦,就是裸的次短路一点不带改的;
懂得都懂~
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 5e5 + 55;
const ll LINF = 0x3f3f3f3f3f3f3f3f;
int n,m;
struct node
{
int u,v;
ll w;
};
struct edge
{
int id;
ll dist;
bool operator < (const edge &a) const
{
return dist > a.dist;
}
}now;
ll dist1[MAXN],dist2[MAXN];
vector<node>e[MAXN];
void dijkstra(int s,int t,ll *dist)
{
for(int i = 1;i <= n;i ++)dist[i] = LINF;
dist[s] = 0;
priority_queue<edge>pq;
pq.push({s,dist[s]});
while(!pq.empty())
{
now = pq.top();pq.pop();
int u = now.id;
if(now.dist > dist[u])continue;
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].v;
ll w = e[u][i].w;
if(dist[v] > dist[u] + w)
{
dist[v] = dist[u] + w;
pq.push({v,dist[v]});
}
}
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i = 1;i <= m;i ++)
{
int u,v;
ll w;
scanf("%d%d%lld",&u,&v,&w);
u ++,v ++;
e[u].push_back({u,v,w});
e[v].push_back({v,u,w});
}
dijkstra(1,n,dist1);
dijkstra(n,1,dist2);
ll ans = LINF;
for(int u = 1;u <= n;u ++)
{
for(int i = 0;i < e[u].size();i ++)
{
int v = e[u][i].v;
ll w = e[u][i].w;
if(dist1[u] + dist2[v] + w > dist1[n])
ans = min(ans,dist1[u] + dist2[v] + w);
}
}
printf("%lld",ans);
}
G题 younik吃大餐(分组dp)
简化题意
younik要去吃饭,餐厅里有n种菜,每个菜有无限多,价格为Vi ,吃第i种菜会增加Wi的快乐值,但是第k次吃这种菜的时,快乐值会减少2k-1点,younik有M块钱,询问如何吃菜获得的快乐值最大;
题解
首先用sum{i,j}表示第i种菜吃了j次获得的快乐值,cost{i,j}表示第i种菜吃了j次的花费;
显然这么表示起来,对于第i种菜只会选择一次去吃或者不吃,这样就转化为 分组dp的题目;
根据分组dp的状态转移即可;
AC code
/*******************************
* Coder : He Shuo. *
* Type : Original Work *
*******************************/
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include<stdio.h>
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<queue>
#include<vector>
#include<deque>
#include<stdlib.h>
#include<set>
#include<bitset>
#include<map>
#include<ctime>
#include<unordered_map>
using namespace std;
typedef long long ll;
const int MAXN = 1e3 + 55;
const ll LINF = 1e16;
const ll mo = 1e9 + 7;
inline ll fpow(ll a,ll b){ll s = 1;a %= mo;while(b){if(b & 1)s = s * a % mo;a = a * a % mo;b >>= 1;}return s;}
int n,M;
int W[105],V[105];
int sum[105][10],cost[105][105];
vector<int>bag[105];
int w[MAXN],val[MAXN];
int cnt = 0;
int dp[MAXN * 10];
void solve()///分组dp
{
int t = n,v = M;
for(int i = 1;i <= n;i ++)
{
for(int k = v;k > 0;k --)
{
for(int j = 0;j < bag[i].size();j ++)
{
int p = bag[i][j];
if(k >= w[p])
{
dp[k] = max(dp[k],dp[k - w[p]] + val[p]);
}
}
}
}
printf("%d",dp[v]);
}
int main()
{
scanf("%d%d",&n,&M);
for(int i = 1;i <= n;i ++)
{
scanf("%d%d",&W[i],&V[i]);
for(int j = 0;j <= 8;j ++)
{
if(j == 0)///第一次吃菜是不会扣快乐值的
{
sum[i][j] = W[i];
cost[i][j] = (j + 1) * V[i];
}
else///不是第一次吃菜会扣快乐值
{
if(W[i] - fpow(2,j - 1) > 0)///保险起见让快乐值都为正数吧;
{
sum[i][j] = sum[i][j - 1] + W[i] - fpow(2,j - 1);
cost[i][j] = (j + 1) * V[i];
}
}
if(sum[i][j] > 0)
{
cnt ++;
bag[i].push_back(cnt);
w[cnt] = cost[i][j];
val[cnt] = sum[i][j];
}
}
}
solve();
}
H题 心怡学姐的围栏
防AK题本蒟蒻当然写不来啦~