A题
Description
题目描述
给N条边,请找三条边,使其组成一个三角形,并使得这个三角形的周长最大。
输入
存在多个样例。 第一行是一个整数N(3≤N;≤10,000),如果N=0,则表示输入结束。 第二行是N个整数,整数处于[1,100000000]之间,为N条边的长度。
输出
输出最大周长三角形的周长,如果不能组成三角形,输出0。
样例输入
5
5 3 4 10 2
0
样例输出
12
Sample Input
Sample Output
//这题其实就是道贪心,怎么贪心呢?只要从第一,第二,第三大的开始贪,然后第二第三第四开始贪。为什么这样子可以呢。应为如果跟这个相邻的两个最大的都不能跟这跟组成三角形,小点就更不可能。代码如下。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10010;
int a[maxn];
int main(){
int n;
while(~scanf("%d",&n)&&n){
for(int i = 0;i < n;i++)scanf("%d",&a[i]);
sort(a,a+n);
__int64 res = 0;
for(int i = n-1;i>=2;i--){
if(a[i]<(a[i-1]+a[i-2])){
res = max(res,(__int64)a[i] + a[i-1] + a[i-2]);
}
}
printf("%I64d
",res);
}
return 0;
}
B题
题目描述
Robb想知道阶乘n!第m位数码是什么?
输入
第一行是一个整数T,(1≤T≤10000)
每行一个样例,为2个整数n,m,0≤n≤1000,1≤m≤log10n!+1
输出
每行输出一个样例的结果
样例输入
3
5 1
5 2
5 3
样例输出
0
2
1
//其实这题就是个大数的乘法,不过简单的大数处理不了,这里就要用到万进制的思想了。跟正常处理差不太多,详细的看下代码吧。当然我看了样神Java大数也能过看个人喜好吧。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int a[maxn];
int Map[maxn][maxn];
int Find(int a, int i, int m)
{
int tp = Map[a][i];
for(int j = 0; j < m - 1; j++)
{
tp /= 10;
}
printf("%d
", tp % 10);
}
int main(){
memset(Map,0,sizeof(Map));
Map[0][0] = 1;
int n = 1;
for(int i = 1;i < 1001;i++){
int tmp = 0;
for(int j = 0;j < n;j++){
Map[i][j] = Map[i-1][j] * i;
Map[i][j] += tmp;
tmp = Map[i][j] / 10000;
Map[i][j] %= 10000;
if(tmp !=0 && j == n-1){
n++;
}
}
a[i] = n;
}
int t;
scanf("%d",&t);
while(t--){
int n,m;
scanf("%d%d",&n,&m);
Find(n,(m-1)/5,m%5==0?5:m%5);
}
return 0;
}
C题
Rank
[ Submit Code ] [ Top 20 Runs ] [ Runs Status ]
Acceteped : 137 Submit : 546
Time Limit : 1000 MS Memory Limit : 65536 KB
Description
题目描述
给你N个数,M次询问,每次询问给定的数在这N个数升序中能排第几(相同数的排名相同)?
输入
第一行输入两个数N(N≤100000),M(M≤100000)。 第二行是N个数,每个数处于[1,109]之间。 第三行是M个数,表示M次询问,每次询问的数为[1,109]之间。
输出
每行输出一次询问的排名,排名从1开始算。
样例输入
5 5
1 1 3 4 5
1 2 3 4 5
样例输出
1
3
3
4
5
//个人觉得这套里面这题最简单,其实就是求下界。可以用lower_bound函数来直接求,不过建议还是自己写下二分的算法吧。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int n,m;
int a[maxn];
int bsearch(int x){
int mid;
int low = 0;
int high = n-1;
while(low <= high){
mid =(low + high)/2;
if(x <= a[mid])high = mid-1;
else low = mid + 1;
}
return low;
}
int main(){
while(~scanf("%d%d",&n,&m)){
for(int i = 0;i < n;i++)scanf("%d",&a[i]);
sort(a,a+n);
for(int i = 0;i < m;i++){
int t;
scanf("%d",&t);
printf("%d
",bsearch(t)+1);
}
}
return 0;
}
D题
Euler
[ Submit Code ] [ Top 20 Runs ] [ Runs Status ]
Acceteped : 94 Submit : 164
Time Limit : 2000 MS Memory Limit : 65536 KB
Description
题目描述
给你一个联通无向图,请问是否可以一笔画出来?
输入
样例的第一行是一个整数T,表示样例的个数。 每个样例的第一行是两个整数N(2≤N≤1,000)和 M(1≤M≤100,000), 分别表示顶点数和边数。顶点编号从1到N,以后的M行为边,每行两个整数, 表示边两个端点的序号。
输出
每行输出一个样例的结果,如果可以一笔画出,输出Yes,否则输出No。
样例输入
2
2 1
1 2
4 3
1 2
1 3
1 4
//其实这道题也是个水题,其实只要判断奇点的个数是不是0或者2,是就可以一笔画,当年离散课王婷老师讲过。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1002;
int a[maxn];
int main(){
int T;
scanf("%d",&T);
while(T--){
int n,m;
memset(a,0,sizeof(a));
scanf("%d%d",&n,&m);
for(int i = 0;i < m;i++){
int t1,t2;
scanf("%d%d",&t1,&t2);
a[t1]++;
a[t2]++;
}
int p = 0;
for(int i = 1;i <= n;i++){
if(a[i]&1)p++;
}
if(p==2||p==0)puts("Yes");
else puts("No");
}
}
E题
一个栈,支持三种操作:
PUSH x ,将x压入栈中
POP,将栈顶弹掉
MIN,输出当前栈中最小值,如果栈为空,输出NULL
给你一个操作列表,请模拟其操作过程。
输入
第一行是一个整数K,表示样例的个数。 每个样例的第一行是一个整数M,表示操作命令的数目1≤M≤100,000。 以后的M行,每行一条命令,栈中所有值处于[0,1000000000]之间。
输出
每个MIN命令输出一个结果,占一行。
样例输入
2
6
MIN
PUSH 2
PUSH 1
MIN
POP
MIN
6
PUSH 3
MIN
PUSH 1
MIN
PUSH 2
MIN
样例输出
NULL
1
2
3
1
1
提示
巨大的输入输出量,请使用C风格的输入输出。
//这题我用的是两个栈,一个栈用来存储数据,另外一个栈顶就是到现在为止出现的最小值。第一个栈其实作用仅仅是用来判断是不是空,第二个栈插入时,如果为空直接插入。否则要跟栈顶进行比较,如果栈顶元素较小就压入栈顶元素,否则就压数据。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 100010;
int m;
int stack1[maxn],stack2[maxn];
int top1,top2;
char s[3][10] = {"PUSH","POP","MIN"};
char a[10];
int main() {
int T;
scanf("%d",&T);
while(T--) {
top1 = 0;
top2 = 0;
scanf("%d",&m);
for(int i = 0; i < m ; i++) {
scanf("%s",a);
if(strcmp(a,s[0])==0) {
int b;
scanf("%d",&b);
stack1[top1++] = b;
if(top2==0) {
stack2[++top2] = b;
} else if(stack2[top2] >=b) {
stack2[++top2] = b;
} else {
stack2[++top2] = stack2[top2-1];
}
} else if(strcmp(a,s[1])==0) {
top1--;
top2--;
} else {
if(top1==0) {
puts("NULL");
} else {
printf("%d
",stack2[top2]);
}
}
}
}
}
F题
题目描述
很多城市人口众多,政府决定在不同城市之间修建高速公路提高相互之间的交通条件。 但是由于修建费用昂贵,所以政府只要能保证所有城市都可以通过高速公路互联就可以了。 但是政府又想这些公路的容量之和尽可能的大。请你设计一下线路,看最大容量和是多少?
输入
第一行是一个整数K,表示样例数。 每个样例的第一行是两个整数N和M(2≤N≤1000;N-1≤M≤10000), N表示N个城市,其中城市代号用1到N表示;M表示可以修建的高速公路条数。 以后的M行为每条高速公路的容量情况。 每行为三个整数X,Y,C,其中1≤X,Y≤N,C≤10^6。
输出
每行输出一个样例的结果,为一个整数。
Sample Input
2
2 1
1 2 1
3 3
1 2 1
1 3 2
2 3 3
Sample Output
1
5
//这题是求最小生成树MST的变形最大生成树,具体参考http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html或者看下离散树上的内容。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
struct edge
{
int u,v,cost;
edge() {}
edge(int x,int y,int z)
{
u=x;
v=y;
cost=z;
}
bool operator <(const edge& a) const
{
return cost>a.cost;
}
};
edge es[20010];
int par[1010];
int n,m;
void init()
{
for(int i=1;i<=n;i++) par[i]=i;
}
int find(int x)
{
return x==par[x]?x:par[x]=find(par[x]);
}
void unite(int x,int y)
{
x=find(x);
y=find(y);
if(x!=y) par[x]=y;
}
long long kruskal()
{
sort(es,es+m);
//for(int i=0;i<m;i++) printf("%d %d %d
",es[i].u,es[i].v,es[i].cost);
long long sum=0;
int count=0;
for(int i=0;i<m;i++)
{
edge e=es[i];
if(find(e.u)!=find(e.v))
{
unite(e.u,e.v);
count++;
sum+=e.cost;
}
}
if(count!=n-1) sum=-1;
return sum;
}
int main()
{
int a,b,c;
int t;
scanf("%d",&t);
while(t--)
{
scanf("%d%d",&n,&m);
init();
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
es[i]=edge(a,b,c);
}
printf("%lld
",kruskal());
}
return 0;
}
G题
题目描述
给你一个序列x1,x2,…,xn,如果数对< xi,xj >,其中i< j,而xi> xj我们称之为逆序数对。 一个序列的逆序数对的数目,称为这个序列的逆序数。 比如说序列 3 1 2 ,逆序数对为 <3,1>和<3,2>,所以这个序列的逆序数为2。 现在给你一个数字序列,请求其逆序数。
输入
每个样例为两行,第一行为一个整数n(n≤10,000),表示序列中数字的个数,如果n为0,则表示输入结束,不需要处理。 第二行是n个整数xi,0≤xi≤100,000。输入数据保证序列中没有相同整数。
输出
每行输出一个整数,表示其序列数。
样例输入
3
3 1 2
4
1 2 3 4
0
样例输出
2
0
//这题其实就是求逆序数,不过由于数据太多不能暴力去解决。这题有两种解法,第一种采用归并算法,应为归并算法的交换次数就是逆序数;或者你用离散化+树状数组去解决。离散化我解释下,比如说1,8,1000000,3的逆序数其实跟1,3,4,2的逆序数一样。这种思想就是用他在里面排行第几就用几代替,然后每次计算时,先把a[i]置1,那么求逆序数就相当于统计a[i+1]~a[n]的和,而树状数组求和的复杂度是o(nlogn)故能求解。还有不理解的话可以看下poj2299的题解,其实你直接拿poj2299的代码就能过.....
/*poj2299原题,,*/
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 500005;
struct Node
{
int val;
int pos;
};
Node node[N];
int c[N], reflect[N], n;
bool cmp(const Node& a, const Node& b)
{
return a.val < b.val;
}
int lowbit(int x)
{
return x & (-x);
}
void update(int x)
{
while (x <= n)
{
c[x] += 1;
x += lowbit(x);
}
}
int getsum(int x)
{
int sum = 0;
while (x > 0)
{
sum += c[x];
x -= lowbit(x);
}
return sum;
}
int main()
{
while (scanf("%d", &n) != EOF && n)
{
for (int i = 1; i <= n; ++i)
{
scanf("%d", &node[i].val);
node[i].pos = i;
}
sort(node + 1, node + n + 1, cmp); //排序
for (int i = 1; i <= n; ++i) reflect[node[i].pos] = i; //离散化
for (int i = 1; i <= n; ++i) c[i] = 0; //初始化树状数组
long long ans = 0;
for (int i = 1; i <= n; ++i)
{
update(reflect[i]);
ans += i - getsum(reflect[i]);
}
printf("%lld
", ans);
}
return 0;
}
H题
题目描述
Estrella是个漂亮的小姑娘,她最喜欢吃的零食就是巧克力,但是巧克力吃多了会发胖,美貌和美食之间她必须做出艰难地选择。
Estrella有N颗巧克力,她按照喜欢的程序给巧克力排好序,并决定在M天内吃完这些巧克力。由于一颗巧克力如果不一次吃完,味道就会变坏,所以她绝对不会把一颗巧克力分开吃。但是每颗巧克力的热量并不相同,Estralla希望知道M天中每天吃的巧克力热量总和最大值。为了尽可能防止发胖,她希望这个值越小越好,请问这个值最小是多少?
输入
第一行是一个整数T(1≤T≤100),表示样例的个数。
每个样例的第一行是两个整数N,M(1≤M≤N≤10000)。
每个样例的第二行是N个整数,表示每颗巧克力的热量,其值处于[1,100000] 之间。
输出
每行输出一个样例的结果。
样例输入
2
5 2
5 3 2 4 1
5 3
5 3 2 4 1
样例输出
8
5
//这题是道二分题。首先关注以下几点:
//
//巧克力要全部吃完
//一块巧克力不能分开吃。
//要按照给定的顺序吃。
//因此,设第i块巧克力的热量为cici。我们可以知道最终答案的范围为[max(ci),∑n1(ci)][max(ci),∑1n(ci)]。这样的话我们采用二分查找的办法来确定最后的答案。对于每一个枚举的值,我们只需判断按照给定顺序吃能否在规定天数内吃完。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 10001;
int a[maxn];
int n,m;
int check(int mid){
int sum = 0;
int i_count = 0;
for(int i = 0;i < n;i++){
sum += a[i];
if(sum > mid){
sum = a[i];
i_count++;
}
if(i_count>=m){
return 0;
}
}
return 1;
}
int main(){
int T;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
int Max = -1;
int sum = 0;
for(int i = 0;i < n;i++){
scanf("%d",&a[i]);
sum += a[i];
Max = max(Max,a[i]);
}
int low = Max;
int high = sum;
int mid;
int ans = 999999;
while(low<=high){
int mid = (low + high)/2;
if(check(mid)){
ans = min(ans,mid);
high = mid - 1;
}
else low = mid + 1;
}
printf("%d
",ans);
}
return 0;
}
I题
题目描述
5的二进制是101,13的二进制是1101,所以在二进制上,5是13的后缀。Lisa获得了一个长长的正整数列表,她想知道在列表中每一个数是列表中多少个其他数的后缀?
输入
第一行是一个整数N,1≤N≤100000,表示整数的个数。 以后N行,每行一个正整数,每个都可以使用一个32位int表示,而且所有的数都是唯一的。
输出
每个整数对应的结果输出一行,
样例输入
5
5
13
1
2
3
样例输出
1
0
3
0
0
//个人觉得这是道字典树模板题。可以先了解下字典树相关知识。
#include<bits/stdc++.h>
using namespace std;
const int maxnode = 100005*32;
const int si = 2;
struct trie{
int ch[maxnode][si];
int num[maxnode];
int sz ;
void clear(){
sz = 1;
memset(ch[0],0,sizeof(ch[0]));
memset(num,0,sizeof(num));
}
void insert(int x){
int u = 0;
int n = 32;
for(int i = 0; i < n&&x;i++){
if(!ch[u][x%2]){
memset(ch[sz],0,sizeof(ch[sz]));
num[sz] = 0;
ch[u][x%2] = sz++;
}
u = ch[u][x%2];
num[u]++;
x/=2;
}
}
int find(int x){
int u = 0;
int n = 32;
for(int i = 0;i < n&&x;i++){
u = ch[u][x%2];
x /=2;
}
return num[u];
}
};
trie t;
int arr[maxnode/32];
int main(){
int n;
while(~scanf("%d",&n)){
t.clear();
for(int i = 0;i < n;i++){
scanf("%d",&arr[i]);
t.insert(arr[i]);
}
for(int i = 0;i < n;i++){
printf("%d
",t.find(arr[i])-1);
}
}
return 0;
}
J题
Description
题目描述
N(3≤N≤1,000)个城市(编号从1~N),M(N-1≤M≤10,000)条公路连接这些城市,每条公路都是双向通车的。 你想从1号城市出发,到达N号城市,期间你希望通过按顺序经过K(0≤K≤3)个指定的城市(这K+2个城市只允许达到1次),求最短的里程。
输入
存在多个样例。 每个样例的第一行是三个整数N,M,K。如果N,M,K为0,则表示输入结束。 以后是M行表示M条公路,每行三个整数x(1≤x≤N),y(1≤y≤N),c(1≤c≤1,000),表示城市x与城市y之间有一条距离为c的公路。输入保证任意两座城市之间至少存在一条路。然后的一行包含K个城市的序号,序号属于[2,N-1]。
输出
每行输出一个样例的结果,为一个整数。如果不存在这样的方案,输出“Impossible”。
样例输入
3 3 1
1 2 3
2 3 4
1 3 2
2
0 0 0
样例输出
7
//这题其实是道最短路的变形,a[0] = 1 a[n] = n对a[i]和a[i+1]求最短路进行相加得出答案。
#include<cstdio>
#include<cstring>
#define inf 99999999
using namespace std;
int n,m,k,mmap[1100][1100],c[20],dis[1100],vis[1100];
void dij(int x,int y)
{
int i,mmin,pos,j;
memset(vis,0,sizeof(vis));
for(i=1;i<=n;i++)
dis[i]=mmap[x][i];
for(i=0;i<=k+1;i++)
{
if(c[i]!=x&&c[i]!=y)
vis[c[i]]=1;
}
for(i=2;i<=n;i++)
{
mmin=inf;
for(j=1;j<=n;j++)
{
if(vis[j]==0&&dis[j]<mmin)
{
mmin=dis[j];
pos=j;
}
}
vis[pos]=1;
if(mmin>=inf) break;
for(j=1;j<=n;j++)
{
if(vis[j]==0&&dis[pos]+mmap[pos][j]<dis[j])
dis[j]=dis[pos]+mmap[pos][j];
}
}
}
int main()
{
int i,j;
while(scanf("%d%d%d",&n,&m,&k)!=EOF&&(n!=0||m!=0||k!=0))
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
mmap[i][j]=inf;
for(i=1;i<=n;i++)
mmap[i][i]=0;
for(i=1;i<=m;i++)
{
int aa,bb,cc;
scanf("%d%d%d",&aa,&bb,&cc);
if(cc<mmap[aa][bb])
mmap[aa][bb]=mmap[bb][aa]=cc;
}
c[0]=1;
for(i=1;i<=k;i++)
scanf("%d",&c[i]);
c[i]=n;
int ans=0,jud=1;
for(i=0;i<=k;i++)
{
dij(c[i],c[i+1]);
// printf("s=%d
",dis[c[i+1]]);
if(dis[c[i+1]]>=inf)
{
jud=0;
break;
}
ans+=dis[c[i+1]];
}
if(jud==0) printf("Impossible
");
else printf("%d
",ans);
}
return 0;
}
K题
题目描述
Eric喜欢旅行,今年暑假终于可以有几天时间出去玩了。他计划在去N个不同的城市,而且不想重复去相同的城市,最后需要回到出发的城市,他想知道怎么走可以让差旅费用降到最低? 我们把城市编号为0~N,Eric总从0号城市出发。
输入
第一行是一个整数K,表示样例的个数。 每个样例的第一行为一个整数N(1≤N≤9),表示想去N个城市。以后的N行,每行N个整数Xij,第i行第j列个整数表示从城市i到城市j所需要的旅费,单次费用不超过1000。i = j 时,Xij = 0。
输出
每行输出一个样例的结果,包括两行,第一行是差旅的总费用,第二行是3个城市的编号序列,每个城市编号之间用一个空格隔开,表示旅行的路线,如果存在多条线路都是最少花费,请按字典序输出这些线路,每个线路一行,最多输出10条线路。
样例输入
1
3
0 1 1 1
2 0 2 2
3 3 0 3
4 4 4 0
样例输出
10
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
//这题可以看出是道DFS的模板题,但是记录路径是个难点。可以在更新时如果大于就从数组下标为0开始更新,否则就从下标i*n开始更新。
#include <bits/stdc++.h>
using namespace std;
int g[10][10], ans, T, path[111], p[10], pid, vis[10], n;
void dfs(int i, int cnt, int cost) {
if(cnt == n) {
if(cost+g[i][0] < ans) {
ans = cost + g[i][0];
pid = 0;
for(int j = 0; j < n; j++) path[pid++] = p[j];
} else if(cost+g[i][0] == ans && pid < n*10) for(int j = 0; j < n; j++) path[pid++] = p[j];
return;
}
for(int j = 1; j <= n; j++) {
if(!vis[j]) {
vis[j] = true;
p[cnt] = j;
dfs(j, cnt+1, cost+g[i][j]);
vis[j] = false;
}
}
}
int main() {
scanf("%d", &T);
while(T--) {
scanf("%d", &n);
for(int i = 0; i <= n; i++) for(int j = 0; j <= n; j++) scanf("%d", &g[i][j]);
ans = 11111, pid = 0;
memset(vis, false, sizeof(vis));
dfs(0,0,0);
printf("%d
", ans);
pid = min(pid, n*10);
for(int i = 0; i < pid; i += n) {
for(int j = i; j < i+n; j++) {
if(j%n) putchar(' ');
printf("%d", path[j]);
}
puts("");
}
}
return 0;
}
L题
题目描述
猜数字游戏是这样的,主持人先从1~N个数字,随意取4个数字,组成一个序列。 然后你猜了K次,每次你依次报4个数字。主持人告诉你数字和位置都对的数字有x个,数字对但位置不对的数字有y个。 请问你满足这K次询问的数字序列有几个?分别是什么?
输入
有多个样例。第一行是一个整数N,(4≤N≤50),如果N为0,表示输入结束,这个样例不用处理。 第二行是一个整数K,(10≤k≤100),表示询问的次数。 以后的K行,每行6个整数,前4个表示你报的数字序列,每个数字在1到N之间,且没有重复,最后两个整数为x和y。(0≤x+y≤N)
输出
每个样例按数字序列的字典序输出满足序列,每个序列占一行,每个整数之间用一个空格隔开,行末没空格。最后输出这些序列的总数。
样例输入
4
1
1 2 3 4 4 0
0
样例输出
1 2 3 4
1
//搜索+暴力
#include <bits/stdc++.h>
int t[4], n, k, cnt, vis[55];
struct node {
int s[4], x, y;
} q[111];
bool judge(int t[4]) {
for(int p = 0; p < k; p++) {
int tx = 0, ty = 0;
for(int i = 0; i < 4; i++)
if(q[p].s[i] == t[i]) tx++;
else for(int j = 0; j < 4; j++) if(q[p].s[i] == t[j]) ty++;
if(q[p].x != tx || q[p].y != ty) return false;
}
return true;
}
void dfs(int pos,int t[4]) {
if(pos == -1) {
if(judge(t)) printf("%d %d %d %d
",t[0], t[1], t[2], t[3]), cnt++;
return;
}
for(int i = 1; i <= n; i++) {
if(!vis[i]) {
vis[i] = 1;
t[3-pos] = i;
dfs(pos-1, t);
vis[i] = 0;
}
}
}
int main() {
while(scanf("%d", &n), n) {
memset(vis, 0, sizeof(vis));
scanf("%d", &k);
for(int i = 0; i < k; i++)
scanf("%d %d %d %d %d %d", q[i].s, q[i].s+1, q[i].s+2, q[i].s+3, &q[i].x, &q[i].y);
cnt = 0;
dfs(3, t);
printf("%d
", cnt);
}
}
M题
题目描述
要过年了,老板准备发年终奖,老板准备根据员工的平时表现对比发放奖金,最低发888,每档再增加1000块。
由于工作表现记录有点问题,可能存在矛盾的描述,所以,如果无法发放的话,则所有人,每人发888元。
老板把这个任务交给你,希望你帮他算出一共需要给多少奖金,每人需要发多少奖金?
输入
第一行是一个整数K,表示样例的个数。 每个样例的第一行是两个整数n(1≤n≤10000)和m(0≤m≤50000),分别表示员工的人数以及工作表现记录的数目,员工的编号从1到n。
以后的m行为两个整数a和b(1≤a≠b≤n),表示员工a的工作表现比b好。
输入数据保证工作表现不会有重复记录。
输出
每个样例先输出一行为奖金总数,然后再输出一行,按员工号的顺序,输出每个员工的奖金数,中间用一个空格隔开。
样例输入
2
3 2
1 2
2 3
3 2
1 2
2 1
样例输出
5664
2888 1888 888
2664
888 888 888
//这题我觉得最难,参考了yzhdd的博客,然后思路是拓补排序,用dfs实现。
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 10010;
vector<int>G[MAXN];
int c[MAXN], n, m, salary[MAXN];
int dfs(int u) {
c[u] = -1;
for(int v = 0; v < G[u].size(); v++) {
int g = G[u][v];
if(c[g] == -1) return -1;
if(!c[g]) salary[g] = dfs(g);
if(salary[g] == -1) return -1;
if(salary[u] < salary[g]+1000) salary[u] = salary[g] + 1000;
}
c[u] = 1;
return salary[u];
}
bool toposort() {
memset(c, 0, sizeof(c));
for(int u = 1; u <= n; u++)
if(!c[u])
if(dfs(u) == -1)
return false;
return true;
}
int main() {
int T;
scanf("%d", &T);
while(T--) {
scanf("%d %d", &n, &m);
for(int i = 0; i <= n ; i++) {
salary[i] = 888;
G[i].clear();
}
for(int i = 0; i < m; i++) {
int u, v;
scanf("%d %d",&u,&v);
G[u].push_back(v);
}
if(toposort()) {
int sum = 0;
for(int i = 1; i <= n; i++) sum += salary[i];
printf("%d
", sum);
for(int i = 1; i <= n; i++)
printf("%d%c", salary[i], i == n ? '
' : ' ');
} else {
printf("%d
", n*888);
while(n--) {
printf("888");
(!n) ? puts("") : putchar(' ');
}
}
}
}