Result | TIME Limit | MEMORY Limit | Run Times | AC Times | JUDGE |
---|---|---|---|---|---|
1s | 8192K | 116 | 23 | Standard |
Go, go, go, it's party time !!! A great many of guests are invited. Guests are numbered from 1 to N. To make the party more joyful, every 3 guests, who are friends to each other, or strangers to each other, will be asked to make a show together. For some preparation reasons, could you please tell me how many shows shall be performed?
Each test case begins with two integers N, M (N <= 1000, M <= 10000). Then M lines follows, each of them will contain two integers a and b, telling that a and b are friends. None of the friend relations will appear more than once in each test case. The inputs terminates when N = M = 0.
For each test case, print the number of shows to be performed in a single line.
3 1 1 2 3 3 1 2 2 3 1 3 5 3 1 2 2 3 1 3 0 0
0 1 4
Problem Source: homeboy
This problem is used for contest: 168
由于对算法知识的匮乏,导致对题目分类并不是很擅长,估且把它放到组合数学里吧。
这是今年计算机10级新生赛(我竟然去参见了)的最后一道题,当时以为是图论题呢,因为对图论的陌生,所以就尝试着暴力来解,结果可想而知,无尽的TLE。
后来飞哥给我讲了这道题的解法,很简单的思路啊,不过要是没想到的话,真的没头绪。
言归正传...
思路:这道题直接来求的话不太好想出高效的算法,所以逆向来考虑,我们来求出不是3人都不认识或者都认识的情况(不妨称之为不完美组合)的数量res,然后用所有的可能情况减去res就得到了我们要的结果了。
具体算法:
设人数是n。
对于每一个人pi来说可能的不完美组合数是和pi是朋友的人数乘以和pi不是朋友的人数结果是resi。
我们假设有三个人A,B,C。
可能的情况(不完美组合情况)是(A+B,A-C,B-C),(A+B,A-C,B+C),(A+B,A+C,B-C)【注:其中+代表两者是朋友关系,-代表不是朋友关系】.
这样当我们按照上面一个一个求resi的时候,就会出现重复的现象。
对于第一种情况(A+B,A-C,B-C),我们再对A和B求resi时都会把(A,B,C)这种组合算成一种不完美组合。
对于第二种情况(A+B,A-C,B+C),我们再对A和C求resi时会把(A,B,C)这种组合算成一种不完美组合。
对于第三种情况(A+B,A+C,B-C),我们再对B和C求resi时会把(A,B,C)这种组合算成一种不完美组合。
当然ABC的不完美组合情况只能是三种情况的一种,但是无论哪种都是对每一种不完美组合都多算了一遍。
所以当我们得到res=res1+res2+res3+...+resi+...+resn之后要令res/=2.这样子就得到了所有不完美组合的情况数量。
然后用所有情况数C(n,3)-res就是最后结果了。
这里还有个问题就是怎么求组合数C(n,m).
当然有两个比较常用的方法
第一种:利用组合数的一个性质即C(n,m)=C(n-1,m)+C(n-1,m-1).递归求解。
第二种:利用组合数定义来C(n,m)=A(n,m)/(m!)来迭代求解。
对于第一种因为这道题说了人数是上限是1000,要是递归太多,加上测试数据多一些的话,一定TLE。
第二种的话的问题就好一些了。
C(n,m)=(n-m+1)/1*(n-m+2)/2*...(n-m+i)/i*...n/m.
这样迭代求就不会超时了。
另外说一下,两个方法都有一个通病,就是都不能算太大的数,所以应用范围有限,但是这个提目只需求C(n,3),所以一定不会超范围。
强调一下,一样的算法我用C就是0.19s,C++就是0.92,看来读入输出的影响不可忽略啊。
继续感谢飞哥的无私讲解。
Code:
<textarea cols="50" rows="15" name="code" class="cpp">/* *JackyZheng *2010/12/04 *C */ #include<stdio.h> #include<string.h> int a[1001]; int combine(int n,int m) { int t=1; for(int i=1;i<=m;i++) { t*=(n-m+i); t/=i; } return t; } int main() { int n; int m; int temp1,temp2; int res; while(scanf("%d%d",&n,&m)) { res=0; if(n==0&&m==0) break; memset(a,0,sizeof(a)); for(int i=1;i<=m;i++) { scanf("%d%d",&temp1,&temp2); a[temp1]++; a[temp2]++; } for(int i=1;i<=n;i++) { res+=a[i]*(n-a[i]-1); } res/=2; printf("%d/n",combine(n,3)-res); } return 0; } </textarea>