• 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
    ",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(' ');
                }
            }
        }
    }
    
  • 相关阅读:
    常用模板
    pascal 的字符串操作
    war2 洛谷模拟赛day2 t3 状压
    状压搜索 洛谷T47092 作业
    Milking Order
    洛谷九月月赛T1 思考
    C数列下标 牛客OI赛制测试赛2
    钓鱼 洛谷p1717
    机房人民大团结(DP)
    Spark的Straggler深入学习(2):思考Block和Partition的划分问题——以论文为参考
  • 原文地址:https://www.cnblogs.com/wlxtuacm/p/6931117.html
Copyright © 2020-2023  润新知