PAT 2020年秋季 7-4 Professional Ability Test (30 分)

Professional Ability Test (PAT) consists of several series of subject tests. Each test is divided into several levels. Level A is a prerequisite (前置要求) of Level B if one must pass Level A with a score no less than S in order to be qualified to take Level B. At the mean time, one who passes Level A with a score no less than S will receive a voucher(代金券)of D yuans (Chinese dollar) for taking Level B.

At the moment, this PAT is only in design and hence people would make up different plans. A plan is NOT consistent if there exists some test T so that T is a prerequisite of itself. Your job is to test each plan and tell if it is a consistent one, and at the mean time, find the easiest way (with minimum total S) to obtain the certificate of any subject test. If the easiest way is not unique, find the one that one can win the maximum total value of vouchers.

Input Specification:

Each input file contains one test case. For each case, the first line gives two positive integers N (≤1000) and M, being the total numbers of tests and prerequisite relations, respectively. Then M lines follow, each describes a prerequisite relation in the following format:

T1 T2 S D

where T1 and T2 are the indices (from 0 to N−1) of the two distinct tests; S is the minimum score (in the range (0, 100]) required to pass T1 in order to be qualified to take T2; and D is the value of the voucher (in the range (0, 500]) one can receive if one passes T1 with a score no less than S and plan to take T2. It is guaranteed that at most one pair of S and D are defined for a prerequisite relation.

Then another positive integer K (≤N) is given, followed by K queries of tests. All the numbers in a line are separated by spaces.

Output Specification:

Print in the first line Okay. if the whole plan is consistent, or Impossible. if not.

If the plan is consistent, for each query of test T, print in a line the easiest way to obtain the certificate of this test, in the format:

T0->T1->...->T

However, if T is the first level of some subject test (with no prerequisite), print You may take test T directly. instead.

If the plan is impossible, for each query of test T, check if one can take it directly or not. If the answer is yes, print in a line You may take test T directly.; or print Error. instead.

Sample Input 1:

8 15
0 1 50 50
1 2 20 20
3 4 90 90
3 7 90 80
4 5 20 20
7 5 10 10
5 6 10 10
0 4 80 60
3 1 50 45
1 4 30 20
1 5 50 20
2 4 10 10
7 2 10 30
2 5 30 20
2 6 40 60
8
0 1 2 3 4 5 6 7

Sample Output 1:

Okay.
You may take test 0 directly.
0->1
0->1->2
You may take test 3 directly.
0->1->2->4
0->1->2->4->5
0->1->2->6
3->7

Sample Input 2:

4 5
0 1 1 10
1 2 2 10
3 0 4 10
3 2 5 10
2 0 3 10
2
3 1

Sample Output 2:

Impossible.
You may take test 3 directly.
Error.

实现思路:

果然pat是一个英文阅读理解考试没差了,基本意思就是判断一个有向图是否是一个拓扑结构:
若是拓扑结构,则判断一个结点是否是入度为0的结点,是的话直接输出You may...,如果不是则输出一条路径使得能够进入该考试的所需总分最小,如果这样的路径不唯一,就输出可以获得最大的代金券的路径,本题比较难理解的就是如果按照关键路径那样的做法,拓扑图中,如果123都是4的前置任务,那么完成4就必须完成123才行,但是本题并不用,只需要完成123其中一项,就可以启动4号任务,这个点自己在考试的时候也卡了好久。
若不是拓扑结构,按照题目输出即可。

题中主要要实现的是,如果测试案例是拓扑图,也就是无环图的情况的时候,需要进行路径的打印和代金券的计算,自己第一次想到的是采用DFS算法,直接进行路径的遍,然后找到一条目标路径,但是部分测试点运行超时了,方法是可行的,但是如果每次查询一个数据T,就需要dfs一次,这样极大占用运存,所以真正解决办法,就是建立一个超点,就是图中有多个入度为0的点,我们就建立一个不存在的超点,把所有度为0的点汇聚起来,设置超点到所有度为0的结点的分数要求和代金券都是0,这样就不会影响结果的,之所以这么做,是因为要采用Dijkstra算法,必须要有一个起点,但是度为0的结点又特别多,所以必须要构建一个结点,把这些度为0的结点都连起来,也就是这个构建出来的结点需要是所有度为0的结点的前置任务。

AC代码:

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
const int N=1010;
const int INF=0x7fffffff;
vector<int> adj[N];
int n,m,k,score[N][N],vou[N][N],in[N]= {0},cpyIn[N]= {0},d[N],vist[N],w[N],pre[N];

bool judge() {
	queue<int> q;
	for(int i=0; i<n; i++) {
		if(cpyIn[i]==0)
			q.push(i);
	}
	int cnt=0;
	while(!q.empty()) {
		int now=q.front();
		cnt++;
		q.pop();
		for(int i=0; i<adj[now].size(); i++) {
			cpyIn[adj[now][i]]--;
			if(cpyIn[adj[now][i]]==0) q.push(adj[now][i]);
		}
	}
	return cnt==n;
}

void dijkstra(int s) {
	fill(d,d+N,INF);
	fill(vist,vist+N,0);
	fill(w,w+N,0);
	d[s]=0;
	for(int i=0; i<=n; i++) {
		int u=-1,MIN=INF;
		for(int j=0; j<=n; j++) {
			if(!vist[j]&&d[j]<MIN) {
				u=j;
				MIN=d[j];
			}
		}
		if(u==-1) return;
		vist[u]=true;
		for(int x=0; x<adj[u].size(); x++) {
			int v=adj[u][x];
			if(!vist[v]) {
				if(d[u]+score[u][v]<d[v]) {
					d[v]=d[u]+score[u][v];
					w[v]=w[u]+vou[u][v];
					pre[v]=u;
				} else if(d[u]+score[u][v]==d[v]&&w[u]+vou[u][v]>w[v]) {
					w[v]=w[u]+vou[u][v];
					pre[v]=u;//保留前置结点 
				}
			}
		}
	}
}

int tag;
void Print(int x) {
	if(x==n) return;
	Print(pre[x]);
	if(tag) printf("->");
	tag=1;
	printf("%d",x);
}

void doFirst() {
	for(int i=0; i<n; i++) {
		if(in[i]==0) {
			adj[n].push_back(i);//建立聚点
			score[n][i]=vou[n][i]=0;//将聚点到每个度为0的结点的权值都设置为0 
		}
	}
	dijkstra(n);
	int val;
	while(k--) {
		scanf("%d",&val);
		if(in[val]==0) printf("You may take test %d directly.\n",val);
		else {
			tag=0;
			Print(val);
			printf("\n");
		}
	}
}

int main() {
	cin>>n>>m;
	int a,b,dis,cost;
	while(m--) {
		scanf("%d%d%d%d",&a,&b,&dis,&cost);
		adj[a].push_back(b);//a的出度边
		score[a][b]=dis;
		vou[a][b]=cost;
		in[b]++;//入度++
		cpyIn[b]++;
	}
	cin>>k;
	if(judge()) {
		printf("Okay.\n");
		doFirst();
	} else {
		printf("Impossible.\n");
		int val;
		while(k--) {
			scanf("%d",&val);
			if(in[val]==0) printf("You may take test %d directly.\n",val);
			else printf("Error.\n");
		}
	}
	return 0;
}

本题照样给出dfs的解题方式,还是有应用的,只不过本题不行,会超时。

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int N=1010;
vector<int> G[N],pre[N];
int n,m,in[N]= {0},inCpy[N]= {0},score[N][N],vou[N][N];

bool judge() {//判断是否是拓扑图
	int vist[N]= {0};
	queue<int> q;
	for(int i=0; i<n; i++)
		if(inCpy[i]==0)
			q.push(i);
	int cnt=0;
	while(!q.empty()) {
		int now=q.front();
		cnt++;
		q.pop();
		for(int i=0; i<G[now].size(); i++) {
			if(!vist[G[now][i]]) {
				inCpy[G[now][i]]--;
				if(inCpy[G[now][i]]==0) {
					q.push(G[now][i]);
				}
			}
		}
	}
	if(cnt==n) return true;
	return false;
}

vector<int> temp,ans;
int minS,maxV;
void dfs(int x) {
	if(in[x]==0) {//找到一条路径
		temp.push_back(x);
		int size=temp.size();
		int tempS=0,tempV=0;
		for(int i=size-2; i>=0; i--) {
			tempS+=score[temp[i+1]][temp[i]];
			tempV+=vou[temp[i+1]][temp[i]];
		}
		if(tempS<minS) {//寻找最短的一条路径
			minS=tempS;
			maxV=tempV;
			ans=temp;
		} else if(tempS==minS&&tempV>maxV) {//如果路径最短的情况下,要求获得最大代金券
			maxV=tempV;
			ans=temp;
		}
		temp.pop_back();
		return;
	}
	temp.push_back(x);
	for(int i=0; i<pre[x].size(); i++)
		dfs(pre[x][i]);
	temp.pop_back();
}

int main() {
	cin>>n>>m;
	int a,b,sc,D,k,query;
	while(m--) {
		scanf("%d%d%d%d",&a,&b,&sc,&D);
		G[a].push_back(b);
		pre[b].push_back(a);
		score[a][b]=sc;
		vou[a][b]=D;
		in[b]++;//计算入度
		inCpy[b]++;
	}
	cin>>k;
	if(judge()) {
		printf("Okay.\n");
		while(k--) {
			scanf("%d",&query);
			if(in[query]==0) {
				printf("You may take test %d directly.\n",query);
			} else {
				minS=0x7fffffff,maxV=-1;
				dfs(query);
				int size=ans.size();
				for(int i=size-1; i>=0; i--) {
					printf("%d",ans[i]);
					if(i>0) printf("->");
					else printf("\n");
				}
			}
		}
	} else {
		printf("Impossible.\n");
		while(k--) {
			scanf("%d",&query);
			if(in[query]==0) printf("You may take test %d directly.\n",query);
			else printf("Error.\n");
		}
	}
	return 0;
}
posted @ 2021-03-10 22:09  coderJ_ONE  阅读(894)  评论(0编辑  收藏  举报