2017程序设计-习题1题解

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\n",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\n", 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\n",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\n",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\n",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\n",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\n", 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\n",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\n",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\n",dis[c[i+1]]);
          if(dis[c[i+1]]>=inf)
          {
              jud=0;
              break;
          }
          ans+=dis[c[i+1]];
        }
        if(jud==0) printf("Impossible\n");
        else printf("%d\n",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\n", 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\n",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\n", 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\n", sum);
            for(int i = 1; i <= n; i++)
                printf("%d%c", salary[i], i == n ? '\n' : ' ');
        } else {
            printf("%d\n", n*888);
            while(n--) {
                printf("888");
                (!n) ? puts("") : putchar(' ');
            }
        }
    }
}
posted @ 2017-06-01 22:33  adfae  阅读(2329)  评论(0编辑  收藏  举报