一道神题……

rzO 发现立杰在初三(http://hi.baidu.com/wjbzbmr/item/4a50c7d8a8114911d78ed0a9据此可以推断)就怒A了此题…… Orz

/*************************************************************

我这种大弱菜,看了题目后完全茫然。

看了剧透后往网络流方向想,结果还是不会T_T。

然后去看题解,还是不会T_T。

然后去看题解的题解,还是不会T_T。

然后想了一个晚上,第二天才有点明白T_T。

上述题解在这里

**************************************************************/

题解【sol】:

请先观摩观摩来自安徽省合肥一中的梅诗珂犇的题解。衷心感谢!

Binary Cat Club (SGU 395)解题报告

安徽省合肥一中梅诗珂

题目大意:有关于一场聚会的N条记录,每条记录的格式有 , , 三种,分别表示进来一个叫”name”的人,出去一个叫”name”的人,当前聚会厅有visitors个人。现在记录的一部分遗失,但剩下记录的顺序是原顺序。给出剩下的记录,请添加最少条数的记录,使之合理化。注意:可以有人进出会厅多次,可以有人聚会结束待在会厅。

约束条件:      1≤N≤200 , 每条 记录中的visitors不超过100。

分析:

首先要明确什么是”合理”,可以看出”合理”有两条要求:

1)           对同一个人,其对应的记录应以 开头,第2个(如果有)一定是 ,第3个(如果有)一定是,交错出现。

2)           任何时候 必须与当前在场人数相同。

显然,不同的人之间互不影响。为了做到第一个要求,我们设关于某人的记录出现的

位置为a1<a2<a3<…<am。分3种情况:

1)       ai对应的记录是,ai+1对应的记录是 ,那么我们一定要在aiai+1之间插入(当ai+1不存在时可不插入)。

2)       ai对应记录 ,ai+1对应的记录是 ,那么有两种可能,一种是中间不插入任何关于name的记录,另一种是插入依次插入 ,如果把 看成左括号, 看成右括号,则两种形式为()和()()。

3)           ai对应记录 ,ai+1对应记录 ,那么我们要在aiai+1之间插

入 。

其实完全可能插入更多的操作,但这样必然会产生完全由新插入的操作组成的

“()”,我们完全可以把其中的name替换成一个从没有出现过的新名字(而逆向替换可

能是非法的)。因此这样考虑简化了问题。

为了达到第二个目标,我们考虑补充一些“新人”,我们可以让所有“新人”的名

字都不相同。

问题转化为:合理选择已有人的 , 操作插入位置,并插入若干“新

人”,从而使任何时候记录真实。

要考虑的情况很多,但是由恰好与visitors相同,想到带上下界的网络流,问题就

不难解决。

设有N1个 记录,对第i(1≤i≤N1),从i点向i+1点连一条上下界皆为

visitors的边,得到的链称为“主链”。

我们要对每一种情况,设计一种结构,“安装”到主链上。

第1种: 设ai在第v个 ,和第v+1个 之间,ai+1uu+1之间,新建一个点p,从pv连边(1,1)表示已有的 记录,vu之间的点向p连边(0,1)表示要插入一个 记录。如下图所示(括号中的第一个数表示下界,第二个表示上界,下同):

第2种: 设ai在第v个 ,和第v+1个 之间,ai+1uu+1之间。新建点p,q,从pv连边(1,1)表示已有的 记录,(v+1)u之间的点向p连边(0,1)(注:应为p(v+1)u之间的点连边(0,1)),从uq连边(1,1)表示已有的 记录,v(u-1)之间的点向q连边(0,1),从qp连边(1,2),表示中间可能还会插入一个“)(”。如下图所示:

第3种: 设ai在第u个 ,和第(u+1)个 之间,ai-1v
v+1之间。新建点q,从uq连边(1,1) 表示已有的 记录,qvu之间的点连边(0,1)表示待插入的 。如下图所示:

(缺了张图)

还要考虑“新人”的插入,我们对主链上的每个点v,新建一个点v’,表示新人的出现在第(v-1)个 ,与第v个 之间,从v’v连边(0,∞)表示在此处可以插入任意多个新人,从(v+1)(N1+1)v’连边(0,∞)表示这些新人对应的 记录。如下图所示:

完成构图,我们只要对其求最小费用可行流即可,由上面论述正确性显然。我们来分析复杂度,主链上有N1个点,对剩下的每个点加上新人对应的点共有(N+ N1)个点,非主链的N个点向主链上每个点各连一条边,最多有 条边,因此我们要对 个点, 条边的图求最小费用流,是可以接受的。

总结:本题的难点是构造出的图并没有源汇,求的也不是最大流,与NOI2008的《志愿者招募》类似,这与大多数网络流问题不同,显示了网络流模型的灵活。在建模过程中有一些小知识:比如串联求最小值,并联求和,点提供一个流量平衡方程,边提供流量限制,想让一条边不出现在最小割中,只需让其容量为无穷大。建立的模型也有几类:如有源汇的最大流,最小费用最大流,带上下界最大(小)流,无源汇最小(大)费用流。提高建模水平,只能靠多练习,解决了问题多推广。】

//////////////////////////////////////////////////////////

然后我再补充几个细节:

  1. 对于上述的第2种,即+-的情况,似乎有一个bug,就是我们没法保证流出来的结果一定是+- or +-+-,可能会出现++--。不过没关系,我们可以把中间的+-看作是新人。
  2. 为了方便,可以在最前面加一个= 0
  3. 对于第一个为-的情况,可以当作是--来处理,也就是在每个名字操作序列的最前面加-。
  4. 对于最后一个为+的情况,可以当作是++来处理,也就是在每个名字操作序列的最后面加+。
  5. 对于新人和最后一个为+的情况,如果是散场的时候可以不用增加费用。
  6. 输出的处理:我们可以将整个操作序列合起来处理,对于某个人单独记录这个人的操作序列已经处理到的位置。对于原有操作就等到循环到了它的时候再输出,对于新增的操作则处理到的时候就输出,不用考虑顺序问题。

P.S 这题我只在sgu395上交过,没有在BZ上交(因为我不是高富帅……交不起)。

附上AC代码和对拍程序

  1. #include <cstdio>
  2. #include <queue>
  3. #include <iostream>
  4. #include <cstring>
  5. #include <algorithm>
  6. #include <vector>
  7. #include <string>
  8. #include <map>
  9. void TLE(){while(1);}
  10. using std::vector;
  11. using std::string;
  12. using std::map;
  13. using std::cout;
  14. using std::endl;
  15. const int N = 800 + 9;
  16. char name[100];
  17. map<string,int>namelist;
  18. struct Operation
  19. {
  20. bool type;
  21. int idx;
  22. string name;
  23. }oper[201][201];
  24. struct Edge{int link,next;}es[N*N*2];
  25. int n,equals,Vc,src,sink,cap[N][N],low[N][N],cost[N][N],dis[N],ec,son[N],opers[N],pre[N],equal[N],new_people,stcap[N][N],delta[N],cnt,name_cnt,type[N],idx[201][201],ans;
  26. vector<string> Out[N];
  27. const string shenben[14]={"wjmzbmr","sevenkplus","acrush","fjxmlhx","lydrainbowcat","fotile","vfleaking","seter","cxjyxxme","hyn","mzn","acaeroligh","thcdusman","black"};
  28. string now("a");
  29. void getnext(string &s)
  30. {
  31. int len = s.size() - 1;
  32. while (len >= 0 && s[len] == 'z') s[len--] = 'a';
  33. if (len < 0) s = "a" + s;
  34. else ++s[len];
  35. }
  36. string newname()
  37. {
  38. while (name_cnt < 14 && namelist[shenben[name_cnt]]) ++name_cnt;
  39. if (name_cnt >= 14) {
  40. while (namelist[now]) getnext(now);
  41. namelist[now] = 1;
  42. return now;
  43. }
  44. namelist[shenben[name_cnt]] = 1;
  45. return shenben[name_cnt];
  46. }
  47. inline void addedge(const int x,const int y)
  48. {
  49. es[++ec].link = y;
  50. es[ec].next = son[x];
  51. son[x] = ec;
  52. }
  53. bool SPFA()
  54. {
  55. static bool inq[N];
  56. static std::queue<int> q;
  57. memset(dis,0x3f,sizeof dis);
  58. inq[src] = 1; dis[src] = 0;
  59. for (q.push(src); !q.empty(); q.pop()) {
  60. const int u = q.front();
  61. inq[u] = 0;
  62. for (int i = son[u]; i; i = es[i].next) {
  63. const int v = es[i].link;
  64. if (cap[u][v] && dis[v] > dis[u] + cost[u][v]) {
  65. pre[v] = u;
  66. dis[v] = dis[u] + cost[u][v];
  67. if (!inq[v]) inq[v] = 1, q.push(v);
  68. }
  69. }
  70. }
  71. return dis[sink] != 0x3f3f3f3f;
  72. }
  73. int Cost_Flow()
  74. {
  75. int ans = 0;
  76. while (SPFA()) {
  77. int Min = 0x7ffffff;
  78. for (int i = sink; i != src; i = pre[i])
  79. Min = std::min(Min,cap[pre[i]][i]);
  80. for (int i = sink; i != src; i = pre[i])
  81. cap[pre[i]][i] -= Min,cap[i][pre[i]] += Min;
  82. ans += Min * dis[sink];
  83. }
  84. return ans;
  85. }
  86. void build_graph()
  87. {
  88. for (int i = 1; i <= equals; ++i)
  89. low[i - 1][i] = cap[i - 1][i] = equal[i];
  90. //会有重边
  91. for (int k = 1; k <= cnt; ++k) {
  92. //oper[j][0].type = 0; // 0 --> '-'
  93. oper[k][opers[k] + 1].type = 1; // 1 --> '+'
  94. oper[k][opers[k] + 1].idx = equals;
  95. for (int i = 0; i <= opers[k]; ++i) {
  96. const int t1 = oper[k][i].type,t2 = oper[k][i + 1].type;
  97. /******************************** ++ *********************************/
  98. if (t1 == 1 && t2 == 1) {
  99. const int u = oper[k][i].idx, v = oper[k][i + 1].idx;
  100. low[Vc][u] += 1; cap[Vc][u] += 1;
  101. for (int j = u; j <= v; ++j) {
  102. cap[j][Vc] += 1;
  103. if (i != opers[k] || j != v) cost[j][Vc] += 1;
  104. }
  105. idx[k][i] = Vc++;
  106. /******************************** -- *********************************/
  107. }else if (t2 == 0 && t1 == 0) {
  108. const int u = oper[k][i].idx, v = oper[k][i + 1].idx;
  109. low[v][Vc] += 1; cap[v][Vc] += 1;
  110. for (int j = u; j <= v; ++j)
  111. cap[Vc][j] += 1,cost[Vc][j] += 1;
  112. idx[k][i] = Vc++;
  113. /******************************** +- *********************************/
  114. }else if (t1 == 1 && t2 == 0) {
  115. const int u = oper[k][i].idx, v = oper[k][i + 1].idx;
  116. low[Vc + 1][Vc] += 1; cap[Vc + 1][Vc] += 2;
  117. low[Vc][u] += 1; cap[Vc][u] += 1;
  118. for (int j = u; j <= v; ++j)
  119. cap[Vc][j] += 1,cost[Vc][j] += 1;
  120. idx[k][i] = Vc++;
  121. low[v][Vc] += 1; cap[v][Vc] += 1;
  122. for (int j = u; j <= v; ++j)
  123. cap[j][Vc] += 1,cost[j][Vc] += 1;
  124. idx[k][i + 1] = Vc++;
  125. }
  126. }
  127. }
  128. new_people = Vc;
  129. for (int i = 0; i < equals; ++i) {
  130. cap[Vc][i] = 10000; //每次最多50人 200 * 50 = 10000
  131. cost[Vc][i] = 1;
  132. for (int j = i + 1; j <= equals; ++j) {
  133. cap[j][Vc] = 10000;
  134. if (j != equals) cost[j][Vc] = 1;//可以不出去
  135. }
  136. ++Vc;
  137. }
  138. for (int i = 0; i < Vc; ++i)
  139. for (int j = 0; j < Vc; ++j)
  140. if (cap[i][j]) {
  141. if (cost[i][j]) cost[j][i] = -cost[i][j];//cost只会一边有值(新图)
  142. addedge(i,j);
  143. if (!cap[j][i]) addedge(j,i);
  144. }
  145. memcpy(stcap,cap,sizeof cap);
  146. /************************ rebuild_graph ************************/
  147. for (int i = 0; i < Vc; ++i)
  148. for (int j = 0; j < Vc; ++j)
  149. if (low[i][j]) {
  150. delta[i] -= low[i][j];
  151. delta[j] += low[i][j];
  152. cap[i][j] -= low[i][j];
  153. }
  154. src = Vc++; sink = Vc++;
  155. for (int i = 0; i < Vc - 2; ++i)
  156. if (delta[i] > 0) cap[src][i] = delta[i],addedge(src,i),addedge(i,src);
  157. else cap[i][sink] = - delta[i],addedge(i,sink),addedge(sink,i);
  158. /************************ rebuild_graph ************************/
  159. }
  160. void out_procedure()
  161. {
  162. static bool tag[201][201];
  163. /************************ Get_Extra_Net ************************/
  164. for (int i = 0; i < Vc; ++i)
  165. for (int j = 0; j < Vc; ++j)
  166. if (low[i][j]) cap[i][j] += low[i][j];
  167. /************************ Get_Extra_Net ************************/
  168. for (int ii = 1; ii <= type[0]; ++ii) {
  169. const int j = type[ii]/201, k = type[ii]%201,i = idx[j][k];
  170. if (tag[j][k]) Out[oper[j][k].idx].push_back("- " + oper[j][k].name);
  171. /************************** ++ ***************************/
  172. if (oper[j][k].type == 1 && oper[j][k + 1].type == 1) {
  173. Out[oper[j][k].idx].push_back("+ " + oper[j][k].name);
  174. for (int l = oper[j][k].idx; l <= oper[j][k + 1].idx; ++l)
  175. if (cap[l][i] != stcap[l][i]) {
  176. if (cost[l][i]) Out[l].push_back("- " + oper[j][k].name);
  177. break;
  178. }
  179. }
  180. /************************** -- ***************************/
  181. else if (oper[j][k].type == 0 && oper[j][k + 1].type == 0) {
  182. for (int l = oper[j][k].idx; l <= oper[j][k + 1].idx; ++l)
  183. if (cap[i][l] != stcap[i][l]) {
  184. Out[l].push_back("+ " + oper[j][k + 1].name);
  185. break;
  186. }
  187. tag[j][k + 1] = 1;
  188. //Out[oper[j][k + 1].idx].push_back("- " + oper[j][k + 1].name);//必须后加//出事了!!
  189. }
  190. /************************** +- ***************************/
  191. else if (oper[j][k].type == 1 && oper[j][k + 1].type == 0) {
  192. Out[oper[j][k].idx].push_back("+ " + oper[j][k].name);
  193. int mem1 = -1,mem2 = -1;
  194. for (int l = oper[j][k].idx; l <= oper[j][k + 1].idx; ++l)
  195. if (cap[i][l] != stcap[i][l]) {mem2 = l;break;}//+
  196. for (int l = oper[j][k].idx; l <= oper[j][k + 1].idx; ++l)
  197. if (cap[l][i + 1] != stcap[l][i + 1]) {mem1 = l;break;}//-
  198. if (mem1 != -1 || mem2 != -1) {
  199. if (mem1 <= mem2) {
  200. Out[mem1].push_back("- " + oper[j][k].name);
  201. Out[mem2].push_back("+ " + oper[j][k].name);
  202. }else {
  203. string tmp = newname();
  204. Out[mem2].push_back("+ " + tmp);
  205. Out[mem1].push_back("- " + tmp);
  206. }
  207. }
  208. //Out[oper[j][k + 1].idx].push_back("- " + oper[j][k].name);//出事了!!
  209. tag[j][k + 1] = 1;
  210. }
  211. }
  212. static string tmp[10001];int st;
  213. for (int i = new_people; i < Vc; ++i) {
  214. if (cap[i][i - new_people] != stcap[i][i - new_people]) {
  215. st = 0;
  216. for (int j = cap[i][i - new_people]; j < stcap[i][i - new_people]; ++j) {
  217. tmp[++st] = newname();
  218. Out[i - new_people].push_back("+ " + tmp[st]);
  219. }
  220. }else continue;
  221. for (int j = i - new_people + 1; j < equals; ++j)
  222. for (int k = cap[j][i]; k != stcap[j][i]; ++k) Out[j].push_back("- " + tmp[st--]);
  223. }
  224. int test = 0;
  225. for (int i = 0; i <= equals; ++i) {
  226. if (i) printf("= %d\n",equal[i]);
  227. test += Out[i].size();
  228. for (vector<string>::iterator iter = Out[i].begin(); iter != Out[i].end(); ++iter)
  229. cout << *iter << endl;
  230. }
  231. //if (test + equals != ans) TLE();
  232. }
  233. int main()
  234. {
  235. #ifndef ONLINE_JUDGE
  236. freopen("395.in","r",stdin);
  237. freopen("395.out","w",stdout);
  238. #endif
  239. scanf("%d",&n);
  240. //equal[0] = 0;
  241. for (int i = 1,x; i <= n; ++i) {
  242. char opt;
  243. scanf("\n%c",&opt);
  244. if (opt == '=') {
  245. scanf("%d",&x);
  246. equal[++equals] = x;
  247. }else {
  248. scanf("%s",name);
  249. int t,mem = cnt;
  250. if (!namelist[string(name)])
  251. namelist[string(name)] = ++cnt;
  252. t = namelist[string(name)];
  253. oper[t][++opers[t]].type = (opt == '+');
  254. oper[t][opers[t]].name = name;
  255. oper[t][opers[t]].idx = equals;
  256. if (cnt != mem) type[++type[0]] = t * 201 + 0;
  257. type[++type[0]] = t * 201 + opers[t]; // for output
  258. }
  259. }
  260. Vc = equals + 1;
  261. build_graph();
  262. ans = Cost_Flow() + n;
  263. printf("%d\n",ans);
  264. out_procedure();
  265. }

  对拍程序:

使用说明:返回值为1则答案错误。0为正确。

  1. #include <cstdio>
  2. #include <cstring>
  3. #include <string>
  4. #include <map>
  5. FILE *f1;
  6. FILE *f2;
  7. FILE *f3;
  8. using std::map;
  9. using std::string;
  10. map<string,int>Map;
  11. char s2[201][30];
  12. int n,m,ans;
  13. int main()
  14. {
  15. f1 = fopen("395_std.out","r");
  16. f2 = fopen("395.out","r");
  17. f3 = fopen("395.in","r");
  18. fscanf(f1,"%d",&ans);
  19. fscanf(f2,"%d\n",&n);
  20. fscanf(f3,"%d\n",&m);
  21. for (int i = 1; i <= m; ++i) fgets(s2[i],100,f3);
  22. if (ans != n) return 1;
  23. char name[30],s1[30];
  24. int cnt = 0,j = 1;
  25. for (int i = 1; i <= n; ++i) {
  26. fgets(s1,100,f2);
  27. if (j <= m && strcmp(s1,s2[j]) == 0) ++j;
  28. if (s1[0] == '=') {
  29. int x;
  30. sscanf(s1,"= %d",&x);
  31. if (x != cnt) return 1;
  32. }else if (s1[0] == '+') {
  33. sscanf(s1,"+ %s",name);
  34. if (++Map[string(name)] > 1) return 1;
  35. ++cnt;
  36. }else if (s1[0] == '-') {
  37. sscanf(s1,"- %s",name);
  38. if (!Map[string(name)]--) return 1;
  39. --cnt;
  40. }
  41. }
  42. if (j != m + 1) return 1;
  43. fclose(f1);fclose(f2);fclose(f3);
  44. }

  Checker.bat

@echo off
:loop
data.exe
395.exe
395_std.exe
checker.exe
if not errorlevel 1 (
  ping -n 2 127.1>nul
  echo correct
  goto loop
)
pause
goto loop

[Sgu395][bzoj2363]Binary Cat Club的更多相关文章

  1. bzoj AC倒序

    Search GO 说明:输入题号直接进入相应题目,如需搜索含数字的题目,请在关键词前加单引号 Problem ID Title Source AC Submit Y 1000 A+B Problem ...

  2. 排序函数 sort() 和 高阶函数sorted()

    · sorted():该函数第一个参数iterable为任意可以迭代的对象,key是用于比较的关键字,reverse表示排序结果是否反转. · L.sort():该函数的三个参数和 sorted() ...

  3. mysql5.7 闪回数据(update delete insert)

    本次测试用Myflash闪回dml操作,有个前提条件是log_bin开启并且log模式是row: mysql> show global variables like "binlog%& ...

  4. hihocoder#1631 : Cats and Fish

    Description There are many homeless cats in PKU campus. They are all happy because the students in t ...

  5. hihoCoder 1631 Cats and Fish(ACM-ICPC北京赛区2017网络同步赛)

    时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU campus. They are all happy ...

  6. 如何设置Android手机的sqlite3命令环境

    1.在PC环境下启动一个模拟器(不是手机) 2.查看模拟器 /systen/xbin是否有sqlite3命令 adb shell cd /system/xbin ls 3.把模拟器 /system/x ...

  7. 一些 Mysql 维护命令

    ----------------------------------------------------------------------------使用mysql客户端程序------------ ...

  8. 2017 ICPC beijing E - Cats and Fish

    #1631 : Cats and Fish 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU camp ...

  9. ACM-ICPC北京赛区2017网络同步赛

    E: Cats and Fish 时间限制:1000ms 单点时限:1000ms 内存限制:256MB 描述 There are many homeless cats in PKU campus. T ...

随机推荐

  1. Http跨域时候预检没通过的几种原因

    网上大多数涉及的原因(直接复制粘帖): CORS把HTTP请求分成两类,不同类别按不同的策略进行跨域资源共享协商. 1. 简单跨域请求. 当HTTP请求出现以下两种情况时,浏览器认为是简单跨域请求: ...

  2. wce.exe getpass.exe 读取密码

    http://www.ampliasecurity.com/research/wce_v1_4beta_x32.zip http://www.ampliasecurity.com/research/w ...

  3. css position的值

    值 描述 absolute 生成绝对定位的元素,相对于 static 定位以外的第一个父元素进行定位. 元素的位置通过 "left", "top", " ...

  4. Revison

  5. linux平台学x86汇编语言学习集合帖

    linux平台学x86汇编语言学习集合帖 linux平台学x86汇编(一):https://blog.csdn.net/shallnet/article/details/45543237 linux平 ...

  6. 原始套接字&&数据链路层访问

    1. 原始套接字能力: (1) 进程可以读写ICMP,IGMP等分组,如ping程序: (2) 进程可以读写内核不处理协议字段的ipv4数据报:如OSPF等: (3) 进程可以使用IP_HDRINCL ...

  7. 【模板】BZOJ 3781: 小B的询问 莫队算法

    http://www.lydsy.com/JudgeOnline/problem.php?id=3781 N个数的序列,每次询问区间中每种数字出现次数的平方和,可以离线. 丢模板: #include ...

  8. STL之顺序容器 deque 动态数组

    deque是一个动态数组,deque与vector非常类似,vector是一个单向开口的连续线性空间,deque则是双向开口的连续线性空间.两者唯一的区别是deque可以在数组的开头和末尾插入和删除数 ...

  9. Java初次见面

    1.Java语言特点(运行环境JRE[操作系统,api,dll]): a.跨平台:Java自带的虚拟机很好地实现了跨平台性.Java源程序代码经过编译后生成二进制的字节码是与平台无关的,但是可被Jav ...

  10. 应用程序有bug崩溃重启的案例

    1.程序主界面代码 using System;using System.Collections.Generic;using System.Diagnostics;using System.Linq;u ...