A-HRZ的序列
1.题意:现有一长度为n的序列,对序列中每个数进行一次操作(加、减或者不变),问存不存在这样一个数,用该数对该序列进行完一次操作后,序列中各元素相等。(每个元素能且只能进行一次操作)
Input
输入第一行是一个正整数 t, 表示数据组数。接下来对于每组数据,输入的第一个正整数n表示a序列的长度,随后一行有n个整数,表示a序列 。
Output
输出共包含t行,每组数据输出一行。对于每组数据,如果存在这样的K,输出"YES",否则输出“NO”。(输出不包含引号)
2.思路:要想让这个序列的每个数都相等,则该序列中不相等的数不能超过3个,所以我们可以使用set容器,将序列存入容器中,然后观察容器中的元素个数,如果数量小于2,则必然满足;如果数量为三,那么如果中位数等于另外两个数之和的1/2,则满足;如果数量大于等于4,则一定不满足。
3.代码:
#include <iostream>
#include <cmath>
#include <set>
#include <string.h>
using namespace std;
set<long long int> p;
long long int a[10001], b[3];
int main()
{
int n,s,count;
int status;
cin>>n;
for(int i=0;i<n;i++)
{
status = 0; s = 0;
cin>>count;
p.clear();
for(int i=0;i<count;i++)
{
cin>>a[i];
p.insert(a[i]);
}
set<long long int> ::iterator it = p.begin();
if(p.size()==3)
{
while(it!=p.end())
{
b[s++] = *it;
it++;
}
if(b[0]+b[2]==2*b[1])
status = 1;
}
if(status||p.size()<=2)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
return 0;
}
B-HRZ学英语
1.题意:现有一个字符串,字符串中包括26个大写字母和特殊符号‘?’,‘?’可以代表任意一个大写字母。问是否存在一个子串,该字符串位置连续且由26个大写字母组成,在这个字串中每个字母出现且仅出现一次,如果存在,则输出从左边开始第一个出现的符合要求的子串,并且要求,如果有多组解同时符合位置最靠左,则输出字典序最小的那一个。不存在,则输出-1。
注:字典序先按照第一个字母,以A、B、C、······、Z的顺序排序;如果第一个字母一样,那么比较第二个、第三个乃至后面的字母,如果比到最后两个单词不一样长,那么把较短的排在前面。例如:
AB??EFGHIJKLMNOPQRSTUVWXYZ
ABCDEFGHIJKLMNOPQRSTUVWXYZ
ABDCEFGHIJKLMNOPQRSTUVWXYZ
上面两种填法都可以构成26字母,但我们要求字典序最小,只能取前者。
Input
输入只有一行,一个符合题目描述的字符串。
Output
输出只有一行,如果存在这样的子串,请输出,否则输出-1
2.思路:该题可参照之前的移动窗口,且窗口大小固定。从i=25开始遍历字符串。同时,我们还要开辟一个长度为26的数组,来判断字符串中的26个字符是否相等并通过该数组来找出特殊符号‘?’对应的字母,然后将‘?’对应的字母替换为指定的字母即可。如果替换后的字符串中各不相同的字母数等于26,就输出该字符串;若遍历完依然找不到就输出-1。
3.代码:
#include <iostream>
#include <string.h>
using namespace std;
char a[26];
int main()
{
string s;
cin>>s;
int len = 0, length;
for(int i=25;i<s.size();i++)
{
length = len+26;
int b[26] = {0};
for(int j=len;j<length;j++)
{
if(s[j]!='?')
{
a[j-len] = s[j];
b[s[j]-65] = 1;
}
}
for(int i=len;i<length;i++)
{
if(s[i]=='?')
{
for(int j=0;j<26;j++)
if(b[j]==0)
{
a[i-len] = j + 65, b[j] = 1;
break;
}
}
}
len++;
int sum = 0;
for(int i=0;i<26;i++)
{
if(b[i]==1) sum++;
}
if(sum==26)
{
for(int i=0;i<26;i++)
cout<<a[i];
return 0;
}
}
cout<<-1;
return 0;
}
C-咕咕东的奇妙序列
1.题意:112123123412345 …这个序列由连续正整数组成的若干部分构成,其中第一部分包含1至1之间的所有数字,第二部分包含1至2之间的所有数字,第三部分包含1至3之间的所有数字,第i部分总是包含1至i之间的所有数字,11212312341234512345612345671234567812345678912345678910,其中第1项是1,第3项是2,第20项是5,第38项是2,第56项是0。现在想知道第 k 项数字是多少。
2.思路:开始使用一层层查找数字的方法,结果发现运行超时。为了避免该情况,我们可以使用二分查找,关键还在于如何计算当前层级包含的数字总数,方法大致为:从1-9算起,依次计算10-99, 100-999的层级数字数之和。每一级的运算过程是先计算出位数相同的数字的数字数,比如说层级为2时,即当前数字最大为99,计算出层为10-99(他们中两位数的个数为1-90)中的数字之和再乘以2,再加上更高层级中所有位数为2的数字数,由此即可计算出二位数的所有数字个数,以此类推。使用二分算法能更高效的求出当前层级和具体数字,最后按照指定格式输出答案即可。
3.代码:
#include <iostream>
#include <cmath>
using namespace std;
long long int n, m, level, num;
long long int s, r, l;
long long int sum(long long int x)
{
return (x + 1) * x / 2;
}
long long int sum1(long long int x)
{
long long int ans = 0, i = 1, j = 1;
for (i = 1, j = 1; j * 10 <= x; i++, j *= 10)
ans += i * sum(j * 9) + i * j * 9 * (x - j * 10 + 1);
return ans + i * sum(x - j + 1);
}
long long int sum2(long long int x)
{
long long int ans=0, i=1, j=1;
for (i=1, j=1 ; j*10<=x; i++,j*=10 )
ans += i*j*9;
return ans + i*(x-j+1);
}
int main()
{
cin>>n;
for (int i = 0; i < n; i++)
{
cin>>m;
l = 0, r = 1e9;
while (l <= r)
{
long long int mid = (l + r) / 2;
if (sum1(mid) < m)
{
l = mid + 1;
level = mid;
}
else
r = mid - 1;
}
m = m - sum1(level++);
l = 0, r = level;
while (l <= r)
{
long long int mid = (l + r) / 2;
if (sum2(mid) < m)
{
l = mid + 1;
num = mid;
}
else
r = mid - 1;
}
m -= sum2(num++);
int a[30] = {0}, j = 0;
while (num != 0)
{
a[j++] = num % 10;
num = num / 10;
}
cout<<a[j-m]<<endl;
}
return 0;
}
Week7作业
A-TT的魔法猫
1.题意:众所周知,TT 有一只魔法猫。
这一天,TT 正在专心致志地玩《猫和老鼠》游戏,然而比赛还没开始,聪明的魔法猫便告诉了 TT 比赛的最终结果。TT 非常诧异,不仅诧异于他的小猫咪居然会说话,更诧异于这可爱的小不点为何有如此魔力?
魔法猫告诉 TT,它其实拥有一张游戏胜负表,上面有 N 个人以及 M 个胜负关系,每个胜负关系为 A B,表示 A 能胜过 B,且胜负关系具有传递性。即 A 胜过 B,B 胜过 C,则 A 也能胜过 C。
TT 不相信他的小猫咪什么比赛都能预测,因此他想知道有多少对选手的胜负无法预先得知,你能帮帮他吗?
Input
第一行给出数据组数。
每组数据第一行给出 N 和 M(N , M <= 500)。
接下来 M 行,每行给出 A B,表示 A 可以胜过 B。
Output
对于每一组数据,判断有多少场比赛的胜负不能预先得知。注意 (a, b) 与 (b, a) 等价,即每一个二元组只被计算一次。
2.思路:本题可直接使用Floyd-Warshall算法完成,同时为了避免分支,应该删去一些不必要的部分。主要在于a[k][j] =a[k][j]||(a[k][x]&&a[x][j])可以分成两步进行,从而避免一些无效的判断,提高代码的效率。
3.代码:
#include <iostream>
using namespace std;
int main()
{
int M, N, count;
int n1, n2, num;
cin>>count;
for(int i=0;i<count;i++)
{
num = 0;
cin>>N>>M;
int a[1000][1000] = {0};
for(int j=0;j<M;j++)
{
cin>>n1>>n2;
a[n1][n2] = 1;
}
for(int i=1;i<=N;i++)
for(int k=1;k<=N;k++)
{
if(a[k][i])
{
for(int j=1;j<=N;j++)
a[k][j] =a[k][j]||a[i][j];
}
}
for(int i=1;i<=N;i++)
for(int j=1;j<=N;j++)
if(!a[i][j]&&!a[j][i]&&i>j)
num++;
cout<<num<<endl;
}
return 0;
}
B-TT的旅行日记
1.题意:众所周知,TT 有一只魔法猫。
今天他在 B 站上开启了一次旅行直播,记录他与魔法猫在喵星旅游时的奇遇。 TT 从家里出发,准备乘坐猫猫快线前往喵星机场。猫猫快线分为经济线和商业线两种,它们的速度与价钱都不同。当然啦,商业线要比经济线贵,TT 平常只能坐经济线,但是今天 TT 的魔法猫变出了一张商业线车票,可以坐一站商业线。假设 TT 换乘的时间忽略不计,请你帮 TT 找到一条去喵星机场最快的线路,不然就要误机了!
输入
输入包含多组数据。每组数据第一行为 3 个整数 N, S 和 E (2 ≤ N ≤ 500, 1 ≤ S, E ≤ 100),即猫猫快线中的车站总数,起点和终点(即喵星机场所在站)编号。
下一行包含一个整数 M (1 ≤ M ≤ 1000),即经济线的路段条数。
接下来有 M 行,每行 3 个整数 X, Y, Z (1 ≤ X, Y ≤ N, 1 ≤ Z ≤ 100),表示 TT 可以乘坐经济线在车站 X 和车站 Y 之间往返,其中单程需要 Z 分钟。
下一行为商业线的路段条数 K (1 ≤ K ≤ 1000)。
接下来 K 行是商业线路段的描述,格式同经济线。
所有路段都是双向的,但有可能必须使用商业车票才能到达机场。保证最优解唯一。
输出
对于每组数据,输出3行。第一行按访问顺序给出 TT 经过的各个车站(包括起点和终点),第二行是 TT 换乘商业线的车站编号(如果没有使用商业线车票,输出"Ticket Not Used",不含引号),第三行是 TT 前往喵星机场花费的总时间。
本题不忽略多余的空格和制表符,且每一组答案间要输出一个换行
2.思路:根据题目已知起点和终点且每条商业线最多乘坐一次。我们可以枚举每一条商业线,计算起点到u的最短路径和v到终点的最短路径再加上该商业先所花费的时间。以起点为原点求单原最短路,存入dis1数组;再以终点为原点求单原最短路,存入dis2数组。枚举商业线(u, v, w),取min{dis1[u]+ dis2[v] +W, dis1[v] +dis2[u]+W},最终再与不走商业线的答案取min,并记录选中的商业线是哪一条。至于路径可以通过递归的方式输出。
3.代码:
#include <iostream>
#include <string.h>
#include <queue>
#include <vector>
#define inf 5*1e8;
using namespace std;
int vis[1001], path[1001], dis1[1001], dis2[1001];
priority_queue<pair<int, int> > q;
struct line
{
int x, y, w;
line(){}
line (int X, int Y, int W)
{
x=X, y=Y, w =W;
}
};
vector<line> head1[1010], head2;
int N, S, E;
void Dijkstra(int a, int *dis)
{
while (!q.empty())
q.pop();
memset(vis, 0, sizeof(vis));
for(int i = 1; i <= N; i++)
dis[i] = inf;
dis[a] = 0, path[a] = 0;
q.push(make_pair(0, a));
while(!q.empty())
{
int x = q.top().second;
q.pop();
if(vis[x])
continue;
vis[x] = 1;
for(int i = 0;i<head1[x].size();i++)
{
int w=head1[x][i].w, b = head1[x][i].y;
if(dis[b]>dis[x]+w)
{
dis[b] = dis[x]+w;
if(a==S)
path[b] = x;
else
path[x] = b;
if((a==S&&b==E)||(a==E&&b==S))
return;
q.push(make_pair(-dis[b], b));
}
}
}
}
void print(int x)
{
if(path[x]!=0)
{
print(path[x]);
cout<<x;
if(x!=E)
cout<<" ";
}
else
cout<<x<<" ";
}
int main()
{
int num, s1, e1, w, l, l1;
while(~scanf("%d %d %d", &N, &S, &E))
{
scanf("%d",&num);
for(int i=0;i<num;i++)
{
scanf("%d %d %d", &s1, &e1, &w);
line a(s1, e1, w);
head1[s1].push_back(a);
line b(e1, s1, w);
head1[e1].push_back(b);
}
Dijkstra(E, dis1);
Dijkstra(S, dis2);
scanf("%d", &num);
for(int i=0;i<num;i++)
{
scanf("%d %d %d", &s1, &e1, &w);
line a(s1, e1, w);
head2.push_back(a);
}
s1 = -1, l1 = dis2[E];
for(int i=0;i<head2.size();i++)
{
l = min(dis1[head2[i].x] + dis2[head2[i].y] + head2[i].w,
dis2[head2[i].x] + dis1[head2[i].y] + head2[i].w);
if(l<l1)
l1 = l, s1 = i;
}
if(s1>=0)
{
if(dis2[head2[s1].x]>dis2[head2[s1].x])
path[head2[s1].x] = head2[s1].y, S = head2[s1].y;
else
path[head2[s1].y] = head2[s1].x, S = head2[s1].x;
}
print(E);
printf("\n");
if(s1>=0)
printf("%d\n", S);
else
printf("Ticket Not Used\n");
printf("%d\n", l1);
for(int i=0;i<=N;i++)
head1[i].clear();
head2.clear();
}
return 0;
}
C-TT的美梦
1.题意:这一晚,TT 做了个美梦!
在梦中,TT 的愿望成真了,他成为了喵星的统领!喵星上有 N 个商业城市,编号 1 ~ N,其中 1 号城市是 TT 所在的城市,即首都。
喵星上共有 M 条有向道路供商业城市相互往来。但是随着喵星商业的日渐繁荣,有些道路变得非常拥挤。正在 TT 为之苦恼之时,他的魔法小猫咪提出了一个解决方案!TT 欣然接受并针对该方案颁布了一项新的政策。
具体政策如下:对每一个商业城市标记一个正整数,表示其繁荣程度,当每一只喵沿道路从一个商业城市走到另一个商业城市时,TT 都会收取它们(目的地繁荣程度 - 出发地繁荣程度)^ 3 的税。
TT 打算测试一下这项政策是否合理,因此他想知道从首都出发,走到其他城市至少要交多少的税,如果总金额小于 3 或者无法到达请悄咪咪地打出 ‘?’。
Input
第一行输入 T,表明共有 T 组数据。(1 <= T <= 50)
对于每一组数据,第一行输入 N,表示点的个数。(1 <= N <= 200)
第二行输入 N 个整数,表示 1 ~ N 点的权值 a[i]。(0 <= a[i] <= 20)
第三行输入 M,表示有向道路的条数。(0 <= M <= 100000)
接下来 M 行,每行有两个整数 A B,表示存在一条 A 到 B 的有向道路。
接下来给出一个整数 Q,表示询问个数。(0 <= Q <= 100000)
每一次询问给出一个 P,表示求 1 号点到 P 号点的最少税费。
Output
每个询问输出一行,如果不可达或税费小于 3 则输出 ‘?’。
2.思路:我们要求的是从1出发到其他点花费的最少的钱可以看做求1到其他点的最短路径。
需要注意的一点是,经过一条道路, 收的税可能是负数,因此图中存在负权边,这是一个含负权边的单源最短路径问题,所以我们需要用SPFA算法来解决问题。我们只需要找出负环和那些通过负环可以到达的点,这些点的最短路径都不存在(通过dfs即可完成),然后按要求输出即可。
3.代码:
#include <iostream>
#include <string.h>
#include <queue>
#include <cmath>
#include <vector>
#define inf 5*1e8
using namespace std;
int dis[300], pre[300], inq[300], cnt[300], a[300];
int n;
queue<int> q;
struct line
{
int x, y, w;
line(){}
line (int X, int Y, int W)
{
x=X, y=Y, w =W;
}
};
vector<line> vec[1000];
void dfs(int x)
{
if(pre[x]==1)
return;
pre[x] = 1, inq[x]=1;
for(int i=0;i<vec[x].size();i++)
dfs(vec[x][i].y);
}
void solve(int x)
{
for (int i = 1; i <= n; ++i)
{
dis[i] = inf;
pre[i] = 0;
inq[i] = 0;
cnt[i] = 0;
}
dis[x] = 0;
inq[x] = 1;
q.push(x);
while (!q.empty())
{
int u = q.front();
q.pop();
inq[u] = 0;
if(pre[u]==1)
continue;
for (int i=0;i<vec[u].size();i++)
{
int v = vec[u][i].y;
if (dis[v] > dis[u] + vec[u][i].w)
{
cnt[v] = cnt[u] + 1;
if (cnt[v] >= n)
dfs(v);
dis[v] = dis[u] + vec[u][i].w;
if (!inq[v])
{
q.push(v);
inq[v] = 1;
}
}
}
}
}
int main()
{
int count1, count2, S, E;
cin>>count1;
for(int i=1;i<=count1;i++)
{
cin>>n;
for(int i=1;i<=n;i++)
{
cin>>a[i];
vec[i].clear();
}
cin>>count2;
for(int i=0;i<count2;i++)
{
cin>>S>>E;
int value = pow(a[E]-a[S], 3);
line a(S, E, value);
vec[S].push_back(a);
}
solve(1);
cin>>count2;
cout<<"Case "<<i<<":"<<endl;
for(int i=0;i<count2;i++)
{
cin>>S;
if(pre[S] == 1||dis[S]<3||dis[S]==inf)
cout<<"?"<<endl;
else
cout<<dis[S]<<endl;
}
}
return 0;
}
Week8作业
A-区间选点②
1.题意:给定一个数轴上的 n 个区间,要求在数轴上选取最少的点使得第 i 个区间 [ai, bi] 里至少有 ci 个点。
使用差分约束系统的解法解决这道题
2.思路:首先我们要构造不等式组,记dis[i]表示数轴上[0,i]之间选点的个数。对于第i个区间[a,b],需要满足dis[b] - dis[a - 1]≥Ci。另外还需要保证sum是有意义的,0≤dis[i] - dis[i - 1]≤1,因此dis[i] - dis[i - 1]≥0且dis[i-1] - dis[i ]≥ -1。求该差分约束系统的最小解,转化为≥不等式组跑最长路,结果为dis[max{b}]。另外注意算法函数的起点是min{a}-1不是0。
3.代码:
#include <iostream>
#include <queue>
#include <vector>
#define inf 5*1e8
using namespace std;
struct edge
{
int to,next,w;
}ed[200001];
int total, max1, min1;
int head[50001], vis[50001], dis[50001];
void add(int x,int y,int w)
{
ed[++total].to=y;
ed[total].next=head[x];
ed[total].w=w;
head[x]=total;
}
void ans(int x)
{
queue<int> q;
for(int i = 0; i <= max1; ++i)
dis[i] = -inf, vis[i] = 0;
q.push(x);
dis[x] = 0, vis[x] = 1;
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = 0;
for(int i = head[u]; i; i = ed[i].next)
{
int v = ed[i].to;
if(dis[v] < dis[u] + ed[i].w)
{
dis[v] = dis[u] + ed[i].w;
if(!vis[v])
{
q.push(v);
vis[v] = 1;
}
}
}
}
}
int main()
{
int n, x, y, w;
cin>>n;
total = 1, min1 = inf, max1 = -1;
for(int i=0;i<n;i++)
{
cin>>x>>y>>w;
add(x-1, y, w);
max1 = max(y, max1);
min1 = min(x, min1);
}
for(int i=min1;i<=max1;i++)
{
add(i-1, i, 0);
add(i, i-1, -1);
}
ans(min1 - 1);
cout<<dis[max1];
return 0;
}
B-猫猫向前冲
1.题意:在一场比赛中,一共有N只猫咪,编号依次为1、2、3、······、N。已知每场比赛的结果求出字典序最小的名次序列。
2.思路:先统计各节点的入度,将入度为0的点压入优先级队列(压入的时候取负,确保得到的是最小节点序),之后依次弹出各节点,并存入名次序列中,如果在移除这一节点(以及这一节点为起点的边)后,与该节点相连的节点的入度变为0的话,则将这个顶点压入优先级队列中,并重复上述过程直至优先级队列为空;之后,我们还要判断名次序列的size()是否为n,若不等于,则图中一定有环路;反之,按序输出名次序列即可。
3.代码:
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int total;
int in[501], head[501];
vector<int> vec;
struct edge
{
int to,next;
}e[50001];
void add(int x,int y)
{
e[++total].to=y;
e[total].next=head[x];
head[x]=total;
}
bool Rank(int n)
{
priority_queue<int> q;
for(int i=1;i<=n;i++)
if(in[i]==0)
q.push(-i);
while(!q.empty())
{
int u = 0 - q.top();
q.pop();
vec.push_back(u);
for(int i = head[u]; i; i = e[i].next)
{
int v = e[i].to;
if(--in[v]==0)
q.push(-v);
}
}
if(vec.size() == n)
return true;
else
return false;
}
int main()
{
int x, y, n, m;
while(scanf("%d%d", &n, &m)!=EOF)
{
vec.clear();
total = 1;
for(int i=1;i<=n;i++)
{
in[i] = 0, head[i] = 0;
}
for(int i=0;i<m;i++)
{
cin>>x>>y;
add(x, y);
in[y]++;
}
if(Rank(n))
{
for(int i=0;i<vec.size();i++)
{
if(i!=0)
cout<<" ";
cout<<vec[i];
}
cout<<endl;
}
}
return 0;
}
C-班长竞选
1.题意:某班竞选班长,N个同学均可发表自己的意见。若意见为A B则表示A认为B合适,而且意见具有传递性,即A认为B合适,B认为C合适,A就认为C合适,要求最高票数和得票最多的人。
2.思路:首先应求出所有的强联通分量SCC,然后再缩点求最高票数。
(1)求SCC用kosaraju算法,第一遍dfs1确定原图的逆后序序列,第二遍dfs2在反图中按照逆后序序列进行遍历(一定要注意是逆后序),每次由起点遍历到的点即构成一个SCC。
(2)缩点:求完SCC后,此时有scant个SCC,且c[i]为顶点i所在的SCC编号。此外还开辟了一个vector数组S[i],用来存放各SCC种的所有顶点。
用vector e来存放缩点图,直接存为反向的,方便之后处理。
(3)求值:缩点后不难发现对于属于第i个SCC的点来说,答案分为两部分,(令SCC[i]表示第i个SCC中点的个数)
当前SCC中的点,ans+=SCC[i]一1(去除自己);
其它SCC中的点,SUM(SCC[j]),其中j可到达i;
即ans=SCC[i]-1+sum(SCC[j]),稍加思考,可以发现最后答案一定出现在出度为0的SCC中。所以对e图中每个入度为0的点进行dfs3,求出对应的max值,即可得到答案,之后再按照要求输出得票数最多的连通分支。
最后结果可能不止一个连通分支,此外应注意各同学的编号是从0 ~ n-1,而不是1 ~ n。
3.代码:
#include <iostream>
#include <queue>
#include <vector>
#include <string.h>
using namespace std;
struct edge
{
int x, y;
};
int vis[5001], f[5001], c[5001], in[5001], k[5001];
int num, scant;
vector<edge> G1[5001], G2[5001], vec[5001];
vector<int> S[5001];
void dfs1(int x)
{
vis[x] = 1;
for(int i = 0; i<G1[x].size(); i++)
{
int y = G1[x][i].y;
if(!vis[y])
dfs1(y);
}
f[num++] = x;
}
void dfs2(int x)
{
c[x] = scant;
for(int i = 0; i<G2[x].size(); i++)
{
int y = G2[x][i].y;
if(!c[y])
dfs2(y);
}
S[scant].push_back(x);
}
void dfs3(int x, int &sum)
{
vis[x] = 1;
sum = sum + S[x].size();
for(int i = 0; i<vec[x].size(); i++)
{
int y = vec[x][i].y;
if(!vis[y])
dfs3(y, sum);
}
}
int main()
{
int T, n, m, x, y;
cin>>T;
for(int i=1;i<=T;i++)
{
cin>> n>> m;
scant = 0, num = 0;
for(int i=0;i<=n;i++)
{
c[i] = f[i] = vis[i] = 0;
G1[i].clear(), G2[i].clear();
}
for(int i=0;i<m;i++)
{
cin>> x>> y;
G1[x].push_back({x, y});
G2[y].push_back({y, x});
}
for(int i=0;i<n;i++)
if(!vis[i])
dfs1(i);
for(int i=num-1;i>=0;i--)
if(!c[f[i]])
scant++, dfs2(f[i]);
for(int i=1;i<=scant;i++)
in[i] = 0, vec[i].clear();
for(int i=0;i<n;i++)
for(int j=0;j<G1[i].size();j++)
{
int begin =G1[i][j].x, end = G1[i][j].y;
if(c[begin]!=c[end])
{
vec[c[end]].push_back({c[end], c[begin]});
in[c[begin]]++;
}
}
int sum = 0, total = 0;
for(int i=1;i<=scant;i++)
{
sum = 0;
memset(vis, 0, sizeof(vis));
if(in[i]==0)
dfs3(i, sum);
total = max(sum, total);
k[i] = sum;
}
priority_queue<int> q;
for(int i=1;i<=scant;i++)
if(k[i]==total)
for(int j=0;j<S[i].size();j++)
q.push(-S[i][j]);
if(scant == 1)
total = S[1].size();
cout<<"Case "<<i<<": "<<total-1<<endl;
int tas = 1;
while(!q.empty())
{
if(!tas)
cout<<" ";
else
tas = 0;
int rank =0 - q.top();
q.pop();
cout<<rank;
}
cout<<endl;
for(int i=1;i<=scant;i++)
S[i].clear();
}
return 0;
}
月模拟题2
路径解析
1.题意:在操作系统中,为了指定文件系统中的某个文件,需要用路径来定位。在类 Unix 系统(Linux、Max OS X、FreeBSD等)中,路径由若干部分构成,每个部分是一个目录或者文件的名字,相邻两个部分之间用 / 符号分隔。
有一个特殊的目录被称为根目录,是整个文件系统形成的这棵树的根节点,用一个单独的 / 符号表示。在操作系统中,有当前目录的概念,表示用户目前正在工作的目录。根据出发点可以把路径分为两类:
绝对路径:以 / 符号开头,表示从根目录开始构建的路径。
相对路径:不以 / 符号开头,表示从当前目录开始构建的路径。
例如,有一个文件系统的结构如下图所示。在这个文件系统中,有根目录 / 和其他普通目录 d1、d2、d3、d4,以及文件 f1、f2、f3、
f1、f4。其中,两个 f1 是同名文件,但在不同的目录下。
对于 d4 目录下的 f1 文件,可以用绝对路径 /d2/d4/f1 来指定。如果当前目录是 /d2/d3,这个文件也可以用相对路径 …/d4/f1 来指定,这里 … 表示上一级目录(注意,根目录的上一级目录是它本身)。还有 . 表示本目录,例如 /d1/./f1 指定的就是 /d1/f1。注意,如果有多个连续的 / 出现,其效果等同于一个 /,例如 /d1///f1 指定的也是 /d1/f1。
本题会给出一些路径,要求对于每个路径,给出正规化以后的形式。一个路径经过正规化操作后,其指定的文件不变,但是会变成一个不包含 . 和 … 的绝对路径,且不包含连续多个 / 符号。如果一个路径以 / 结尾,那么它代表的一定是一个目录,正规化操作要去掉结尾的 /。若这个路径代表根目录,则正规化操作的结果是 /。
注意:
若路径为空字符串,则正规化操作的结果是当前目录。
Input
第一行包含一个整数 P,表示需要进行正规化操作的路径个数。
第二行包含一个字符串,表示当前目录。
以下 P 行,每行包含一个字符串,表示需要进行正规化操作的路径。
Output
共 P 行,每行一个字符串,表示经过正规化操作后的路径,顺序与输入对应。
2.思路:因为要输出各条路径的绝对路径,因此必须获得各文件的名称,所以要开辟vector< string >类型的数组来存储各个文件名;又因为输入中有相对路径和绝对路径,因此需要分别记录两条路径,为了操作方便,处理时对两个数组同时进行操作,然后按要求输出指定的路径即可。至于路径中文件名的存储(需要注意标记文件名的截止),遍历输入路径即可完成,需要注意的就是当文件名为"…"时,需要弹出前一个压入的文件名,这个通过pop_back()函数即可实现。
3.代码:
#include <iostream>
#include <string.h>
#include <vector>
using namespace std;
vector<string> a, b, c;
char s2[1001];
int num, status;
void copy(vector<string> &v1,vector<string> &v2)
{
v2.clear();
for(int i=0; i<v1.size(); i++)
v2.push_back(v1[i]);
}
void output(vector<string> &v)
{
if (v.empty())
printf("/");
for(int i=0; i<v.size(); i++)
cout<<"/"<<v[i];
}
void split(string s1)
{
num = 0, status = 1;
int i = 0;
while(s1[i]==' ')
i++;
if(i==s1.size()||s1[0]!='/')
status = 0;
while(true)
{
int j = 0, c = 0;
while (s1[i] == '/'&&i<s1.size())
i++;
while (s1[i] != '/'&&i<s1.size())
{
s2[j] = s1[i];
if(s1[i]=='.')
c++;
j++, i++;
}
s2[j] ='\0';
if (c == 2)
{
if(!a.empty())
a.pop_back();
if(!b.empty())
b.pop_back();
}
else if (c == 1)
continue;
else if(j != 0)
b.push_back(s2), a.push_back(s2) ;
if(i==s1.size())
return;
}
}
int main()
{
string s;
int p;
cin>>p;
cin.get();
getline(cin, s);
split(s);
copy(a, c);
b.clear();
for (int i = 0; i < p; i++)
{
copy(c, a);
getline(cin, s);
split(s);
if (status == 0)
output(a);
else
output(b);
cout<<endl
b.clear();
}
return 0;
}