ZOJ 3879 Capture the Flag
以此题纪念我写的第一篇acm博客,第一道模拟:)
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3879
题意非常复杂,感觉好像在写一个游戏似的,一大堆规则,还全部都是纯模拟,一点算法都没有,全考代码的……
废话结束,开始说题——
第一行输入t,表示有t组数据
每组数据,第一行输入n, q, s, c,表示有n组队伍,每队q个地点,每队初始s分,一共c次操作
接下来c组操作……
每组操作,第一行输入m, 表示m次成功攻击
接下来m次攻击……每次输入a, b, c,表示a攻击b的c地点。
接下来输入q行,每行n个数,表示每队伍的每个地点的状态,1表示好,0表示不好……
接下来一行输入d,表示d次查询
接下来一行输入d个数,表示查询d组队的得分情况,并查询这几组队的排名……
规则:
攻击n组队,每队q个地点。
每一回合(即每次操作):
如果第a队共有b个地点被攻击,无论多少队伍攻击他,每个地点十七失去(n-1)分,一共失去b*(n-1)分。攻击那个地点的队伍平均获得那个地点失去的(n-1)分。
如果第a队的c地点状态为“不好”,则a队失去(n-1)分.
如果第a队的c地点状态为“好”,c地点状态不好的队伍的个数为k,则a队获得(n-1)*k/(n-k);
如果两支队伍的得分之差<0.00001,则认为两支队伍排名相同……
(坑死爹的规则啊啊啊啊啊啊)
写这道题要注意——
1. 判断那个0.00001
2. 如果在同一回合,a队攻击了b队的c地点多次,则只认为他攻击了一次
3. 下一回合各个地点的状态重置……
ps.写完发现……
1. 其实这道题我的姿势很挫,但最终还是过了……谢天谢地……
2. 题很水,我也很水
#include <cstdio> #include <cstring> #include <cmath> #include <algorithm> using namespace std; const int N = 110; const int NN = 20; struct node //存各支队伍的信息 { bool q[NN]; //个地点的状态 double point; //得分 bool ed[NN][N]; //c点是否被b队攻击 bool edp[NN]; //c点是否被攻击 int eds[NN]; //c点被攻击了几次 }tm[N]; struct node1 //按得分排序使用,这是最搓的地方…… { int flag; double p; int r; }sorting[N]; int t; int n, q, c; double s; int sum[NN]; //存c地点良好状态的队伍数 bool cmp(node1 x, node1 y) { return x.p - y.p > 0.00001; //坑爹的0.00001 } void update() //数据初始化 { for(int i = 0; i < n; i++) { memset(tm[i].q, 0, sizeof(tm[i].q)); memset(tm[i].ed, 0, sizeof(tm[i].ed)); memset(tm[i].edp, 0, sizeof(tm[i].edp)); memset(tm[i].eds, 0, sizeof(tm[i].eds)); } memset(sum, 0,sizeof(sum)); } int main() { //freopen("test.txt", "r", stdin); scanf("%d", &t); while(t--) { scanf("%d%d%lf%d", &n, &q, &s, &c); for(int i = 0; i < n; i++) { memset(tm[i].q, 0, sizeof(tm[i].q)); memset(tm[i].ed, 0, sizeof(tm[i].ed)); memset(tm[i].edp, 0, sizeof(tm[i].edp)); memset(tm[i].eds, 0, sizeof(tm[i].eds)); memset(sum, 0, sizeof(sum)); tm[i].point = s; } for(int i = 0; i < c; i++) //c次操作 { int midn; scanf("%d", &midn); //midn次攻击 for(int j = 0; j < midn; j++) { int a, b, qc; scanf("%d%d%d", &a, &b, &qc); tm[b-1].edp[qc-1] = 1; if(!tm[b-1].ed[qc-1][a-1]) //判断是否重复攻击(ac前最后一次wa) { tm[b-1].ed[qc-1][a-1] = 1; tm[b-1].eds[qc-1]++; } } if(midn > 0) //如果有攻击,则算分(也不知道是不是有效剪枝) { for(int j = 0; j < n; j++) //n点被攻击 { for(int k = 0; k < q; k++) { if(tm[j].edp[k] == 1) //k点被攻击 { tm[j].point -= 1.0*(n-1); for(int l = 0; l < n; l++) { if(tm[j].ed[k][l] == 1) //l队攻击 { tm[l].point += 1.0*(n-1)/tm[j].eds[k]; } } } } } } for(int j = 0; j < q; j++) //各队个点状态判断 { int mid; for(int k = 0; k < n; k++) { scanf("%d", &mid); if(mid == 1) //状态不错 { tm[k].q[j] = 1; sum[j]++; //j点状态好的数目++ } } for(int k = 0; k < n; k++) //算分 { if(tm[k].q[j] == 0) tm[k].point -= 1.0*(n-1); else tm[k].point += 1.0*(n-1)*(n-sum[j])/sum[j]; } } int cq; scanf("%d", &cq); //cq次查询 if(cq > 0) //又是一个不知道有没有用的剪枝…… { for(int j = 0; j < n; j++) { sorting[j].p = tm[j].point; sorting[j].flag = j; } sort(sorting, sorting+n, cmp); sorting[0].r = 1; for(int j = 1; j < n; j++) { if(fabs(sorting[j-1].p-sorting[j].p) < 0.00001 ) sorting[j].r = sorting[j-1].r; //又是0.00001 else sorting[j].r = j+1; } for(int j = 0; j < cq; j++) { int miding; scanf("%d", &miding); printf("%.8f", tm[miding-1].point); for(int k = 0; k < n; k++) //最搓的地方又一次出现,寻找对应队伍的排名,效率低的要死(居然这么挫……推荐大家看看别人的排序方法,我这个实在……但是我实在不想改了) { if(sorting[k].flag == miding-1) printf(" %d\n", sorting[k].r); } } } update(); } } return 0; }
附上别人对队伍排名的操作——
1 struct node1 2 { 3 int r; //排名 4 double p; //得分 5 int s; //下标 6 }sorting[N]; 7 8 bool cmp1(node1 x, node1 y) 9 { 10 return x.p - y.p > 0.00001 //按得分排名 11 } 12 13 bool cmp2(node1 x, node1 y) 14 { 15 return x.s < y.s; //按下标排序 16 } 17 18 for(int i = 0; i < n; i++) 19 { 20 sorting[i].p = tm[i].p; 21 sorting[i].s = i; 22 } 23 24 sort(sorting, sorting+n, cmp1); 25 26 sorting[0].r = 1; 27 for(int i = 1; i < n; i++) 28 { 29 if(fabs(sorting[i]-sorting[i-1]) < 0.00001) sorting[i].r = sorting[i-1].r; 30 else sorting[i].r = i+1; 31 } 32 33 sort(sorting, sorting+n, cmp2);