• BestCoder Round #92 1002 Count the Sheep —— 枚举+技巧


    题目链接:http://bestcoder.hdu.edu.cn/contests/contest_showproblem.php?cid=748&pid=1002


    题解:

    做题的时候只是想到用dfs暴搜,结果超时了。(刚学dfs时以为它无所不能,后来渐渐不太喜欢了,因为太暴力了,经常超时)

    TLE wa代码如下:

    #include<bits/stdc++.h>  
      
    using namespace std;  
      
    vector<int>v0[100005],v1[100005];  
    int vis[2][100005],ans;  
      
    void dfs(int k, int flag, int step)//flag标记男女,0为男,1为女。  
    {  
        if(step==4) {ans++; return ;}  
      
        if(flag)  
        {  
            int sum = v1[k].size();  
            for(int i = 0; i<sum; i++)  
            if(!vis[0][v1[k][i]])  
            {  
                vis[0][v1[k][i]] = 1;  
                dfs(v1[k][i],0,step+1);  
                vis[0][v1[k][i]] = 0;  
            }  
        }  
      
        else  
        {  
            int sum = v0[k].size();  
            for(int i = 0; i<sum; i++)  
            if(!vis[1][v0[k][i]])  
            {  
                vis[1][v0[k][i]] = 1;  
                dfs(v0[k][i],1,step+1);  
                vis[1][v0[k][i]] = 0;  
            }  
        }  
    }  
      
    int main()  
    {  
        int n,m,k,T,t0,t1;  
        scanf("%d",&T);  
        while(T--)  
        {  
            ans = 0;  
            scanf("%d%d%d",&n,&m,&k);  
            for(int i = 1; i<=n; i++) v0[i].clear();  
            for(int i = 1; i<=m; i++) v1[i].clear();  
            memset(vis,0,sizeof(vis));  
            for(int i = 0; i<k; i++)  
            {  
                scanf("%d%d",&t0,&t1);  
                v0[t0].push_back(t1);  
                v1[t1].push_back(t0);  
            }  
      
            for(int i = 1; i<=n; i++)  
            {vis[0][i] = 1; dfs(i,0,1); vis[0][i] = 0;}  
            for(int i = 1; i<=m; i++)  
            {vis[1][i] = 1; dfs(i,1,1); vis[1][i] = 0;}  
      
            printf("%d
    ",ans);  
        }  
        return 0;  
    }  
    



    后来大致看了题解,就觉得可以逐个点枚举,从第一个到第四个,用for循环,但仔细想想,这跟dfs没用本质的区别,只不过递推与递归的实现不同。

    于是又认真看了题解。发现他不是从第一个点开始枚举的,而是从第二第三个点枚举的,为什么呢?其实画一下图就可以很好理解。四个点构成三条边,如果从第一个点开始枚举,那么所有人都必须知道朋友是谁,才能继续枚举下去,用图的话来说,是只有知道下一个点是谁,才能构成线段,所以只能一个一个枚举。但是如果从第二第三个开始枚举,即从中间的那条边开始,即枚举中间那条边,那么只需要一个性别知道朋友是谁和个数,另一个性别知道朋友的个数,则直接可以分别用(个数-1),即减去当前已确定的对方,就是剩下的对方,然后相乘。

    上述解释可能很模糊,但可以抽象成一种思维方法。就好像第二个点和第三个点处在中间,其两边都还连有点,那么这两个点的地位相对第一第四个点来说要刚,因为其起到的作用,或影响范围更广。所以就挑选影响范围广的进行操作,那么气效率也更高。


    学习之处:大致来讲,挑选影响范围广的进行操作,那么其效率会更高。

    代码如下:

    #include<bits/stdc++.h>  
      
    using namespace std;  
      
    vector<int>v[100005];//v记录男生的朋友  
    int cnt[100005];//cnt记录女生的朋友个数  
    int main()  
    {  
        int T, n,m,k,x,y;  
        scanf("%d",&T);  
        while(T--)  
        {  
            scanf("%d%d%d",&n,&m,&k);  
            memset(cnt,0,sizeof(cnt));  
            for(int i = 1; i<=n; i++)  
                v[i].clear();  
      
            for(int i = 0; i<k; i++)  
            {  
                scanf("%d%d",&x,&y);  
                v[x].push_back(y);  
                cnt[y]++;  
            }  
      
            long long ans = 0;//用int会溢出  
      
            for(int i = 1; i<=n; i++)//枚举男生,即第三个点  
            {  
                int sum = v[i].size();//这个男生的朋友个数  
                for(int j = 0; j<sum; j++)//枚举与之为朋友的女生,即第二个点  
                {  
                    /*这条公式非常精彩。其实第一第二个循环实际是枚举中间那条边。 
                    首先中间的边已经确定,那么只需要知道第二第三个点的朋友个数, 
                    然后再减去对方,就是剩下的朋友数,再相乘,就是在中间的边已确立 
                    的情况下,不同连线方式的个数。*/  
                    ans += (cnt[v[i][j]]-1) * (sum-1);  
                }  
            }  
      
            printf("%lld
    ",ans*2);  
        }  
        return 0;  
    }  
    
    


  • 相关阅读:
    Dos命令大全(收藏)
    asp.net读写Cookies
    asp.net文件下载
    使用存储过程分页
    (十)死锁检测算法
    poj1664
    一席话惊醒梦中人
    深入了解scanf()/getchar()和gets()/cin等函数
    小结《malloc与new之区别》
    (六)文件管理
  • 原文地址:https://www.cnblogs.com/DOLFAMINGO/p/7538766.html
Copyright © 2020-2023  润新知