第四届ACM_DIY群程序设计竞赛 (部分解题报告) 弱菜在此大牛无视。。。
http://ac.jobdu.com/problem.php?pid=1421
第一题:题意就是给定n个点每个点可能带表Female或者Male,以及给出各个节点的关系矩阵,让你求出现 AbOr的期望, 、
AbOr这样定义:
1:必须是Male;
2:他至少有m个Female朋友;
刚开始我一直以为求的是AbOr出现的概率,所以显示按每个节点两种状态(F ,M)爆搜所有可能的情况然后除以总的可能(2^N)样例竟然都过了,提交直接TLE因为本菜没有注意到T = 10000 这样复杂度就成了10^4*2^20了,指定超时。后来想到了循环每个节点,然后检查他的哦鞥有节点只有他的朋友节点大于m 才有可讨论性,假设他的朋友节点为ct个,那么他所有的可能是 c(ct,m) ,c(ct,m + 1) , ct(ct,m + 2) , ...... , c(ct,ct);而他的概率全都是 (1/2)(当前节点肯定是M)*(1/2^i) *(1/2^(ct - i)) (m<=i <= ct) 及概率p = 1/2^(ct+1);而且每个情况得概率相同。这样再根据期望公式 E(X) = X1*p(X1) + X2*p(X2) + …… + Xn*p(Xn)求即可:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define maxn 55 using namespace std; int pow2[maxn]; double c[maxn][maxn]; int n,m; char str[maxn][maxn]; int main() { int i,j,t; //求2的幂 for (i = 0; i < 21; ++i) pow2[i] = 1<<i; //求组合 for (i = 0; i < 21; ++i) c[i][i] = c[i][0] = 1; for (i = 2; i < 21; ++i) { for (j = 1; j < i; ++j) { c[i][j] = c[i - 1][j - 1] + c[i - 1][j]; } } scanf("%d",&t); while (t--) { scanf("%d%d",&n,&m); for (i = 0; i < n; ++i) scanf("%s",str[i]); if (n ==0) { printf("0.00\n"); continue; } double sum = 0,ans = 0; for (i = 0; i < n; ++i) { int ct = 0; sum = 0; //遍历他的朋友 for (j = 0; j < n; ++j) { if (str[i][j] == '1') ct++; } if (ct >= m) { //求和后乘以概率 for (int k = m; k <= ct; ++k) { sum += c[ct][k]; } ans += (1.0/pow2[ct + 1])*sum; } } printf("%.2lf\n",ans); } return 0; } /************************************************************** Problem: 1421 User: gbaoxing Language: C++ Result: Accepted Time:10 ms Memory:1536 kb ****************************************************************/
http://ac.jobdu.com/problem.php?pid=1422
第二题:
这道题啊,问了虎哥才做出来的,分别开两个数组记录当前节点左边比他小的最近位置,右边比他小的最近位置,然后比较左右距离大小输出即可;
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #define maxn 1000009 using namespace std; int a[maxn],l[maxn],r[maxn]; int n; int Abs(int x) { if (x < 0) return -x; else return x; } int main() { int i,t; scanf("%d",&t); while (t--) { scanf("%d",&n); memset(a,0,sizeof(a)); scanf("%d",&a[1]); //查找左边的最近的比他小的位置 l[1] = 0; for (i = 2; i <= n; ++i) { scanf("%d",&a[i]); int pos = i - 1; while (a[pos] >= a[i]) { pos = l[pos]; if (pos == 0) break; } if (pos == 0) l[i] = 0; else l[i] = pos; } //查找右边的最近的比他小的位置 r[n] = 0; for (i = n - 1; i >= 1; --i) { int pos = i + 1; while (a[pos] >= a[i]) { pos = r[pos]; if (pos == 0) break; } if (pos == 0) r[i] = 0; else r[i] = pos; } //遍历输出 for (i = 1; i < n; ++i) { if (l[i] !=0 && r[i] != 0) { int t; if (Abs(l[i] - i) <= Abs(r[i] - i)) t = l[i]; else t = r[i]; printf("%d ",a[t]); } else { if (l[i] == 0 && r[i] == 0) printf("0 "); else { if (l[i] != 0) printf("%d ",a[l[i]]); else printf("%d ",a[r[i]]); } } } if (l[n] !=0 && r[n] != 0) { int t; if (Abs(l[n] - n) <= Abs(r[n] - n)) t = l[n]; else t = r[n]; printf("%d",a[t]); } else { if (l[n] == 0 && r[n] == 0) printf("0"); else { if (l[n] != 0) printf("%d",a[l[n]]); else printf("%d",a[r[n]]); } } printf("\n"); } return 0; } /************************************************************** Problem: 1422 User: gbaoxing Language: C++ Result: Accepted Time:1610 ms Memory:13228 kb ****************************************************************/
第四题:http://ac.jobdu.com/problem.php?pid=1424
题意是给定n个圆,m个描述,x y表示编号为x的圆要套y个圆,题目给出如果重复嵌套只算最外层的嵌套。这道题目trick大大的多,首先看
1:1≤n≤16,0≤m≤100 这里m可能远大于n所以会出现 形如下面的数据
1 2
1 4
1 6 这样不确定的话输出NO
如果是:
1 2
1 2 的重复数据要记得去重;
2:. (1≤x≤n,|y|≤1000)y还有可能负值,并且有可能大于 n - 1
所以如果出现这样的数据也指定不可能了。
才开始我使用两个栈来模拟的,s1放需要套圆的圆(即y != 0) s2放不需要套圆的圆(即y ==0) 每次从s2中取y个给s1中的圆套,这样就满足了,然后s1的那个出栈,y ==0 压入s2变为可被套的、、知道s2为空即可,我在处理向阳里给的第三组数数据时,直接从m 到 n处理未给定的节点了,其实应该从s1.size() + s2.size()开始的,改后总算A了,整死我了。
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <stack> #define maxn 108 using namespace std; struct node { int x; int num; }stock[maxn]; stack<node>s1,s2; int n,m,len; int isok(node z) { for (int i = 0; i < len; ++i) { if (stock[i].x == z.x && stock[i].num != z.num) return -1;//判断trick if (stock[i].x == z.x && stock[i].num == z.num) return 0;//去重 } stock[len++] = z; return 1; } int main() { int i,t; scanf("%d",&t); while (t--) { len = 0; while (!s1.empty()) s1.pop(); while (!s2.empty()) s2.pop(); scanf("%d%d",&n,&m); node p; bool flag = false; for (i = 0; i < m; ++i) { scanf("%d%d",&p.x,&p.num); int mark = isok(p); if (mark == -1) flag = true; else if (mark == 1) { if (p.num) s1.push(p); else s2.push(p); } if (p.num > n - 1 || p.num < 0) flag = true; } if (flag) { printf("NO\n"); continue; } //注意起点。。 for (i = s1.size() + s2.size(); i < n; ++i) { p.num = 0; s2.push(p); } //两个栈的模拟 while (!s1.empty() && !s2.empty()) { node tmp = s1.top(); s1.pop(); if (tmp.num > s2.size()) { flag = true; break; } else { for (i = 0; i < tmp.num; ++i) { s2.pop(); } tmp.num = 0; s2.push(tmp); } } if (s1.empty() && !flag) printf("YES\n"); else printf("NO\n"); } return 0; } /************************************************************** Problem: 1424 User: gbaoxing Language: C++ Result: Accepted Time:10 ms Memory:1516 kb ****************************************************************/
其实还有一个更容易的办法就是判完trick后,把所有满足条件并且去重之后的y相加,判断是否<= n -1 如果成立YES 否则NO 不知道怎么证明:
#include <iostream> #include <cstring> #include <cstdio> #include <algorithm> #include <stack> using namespace std; struct node { int x; int y; }p[108]; int n,m,len; int isok(node z) { for (int i = 0; i < len; ++i) { if (p[i].x == z.x && p[i].y != z.y) return -1; if (p[i].x == z.x && p[i].y == z.y) return 1; } p[len++] = z; return 0; } int main() { int i,t; int x,y; scanf("%d",&t); while (t--) { len = 0; scanf("%d%d",&n,&m); bool flag = false; node q; int ct = 0; for (i = 0; i < m; ++i) { scanf("%d%d",&x,&y); q.x = x; q.y = y; int mark = isok(q); if (mark == -1) flag = true; else if (mark == 0) ct += y; if (y > n - 1 || y < 0) flag = true; } if (flag) { printf("NO\n"); continue; } if (ct <= n - 1) printf("YES\n"); else printf("NO\n"); } return 0; } /************************************************************** Problem: 1424 User: gbaoxing Language: C++ Result: Accepted Time:10 ms Memory:1512 kb ****************************************************************/