CSP2+作业7、8+月模拟题2

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值