Topoi(一个经常会炸的网站)

本题提交链接

很久以前的题目了, 刚开了博客,来写一波题解

先看一波提交记录:

调了好几天QAQ

唉! 要是这些高手里有我估计直接 输出1 就AC了

算法

DFS + 优化剪枝(用了一点状态压缩)

剪枝

1:求最小值很容易向导最优化剪枝

if(ws>=ans) return;  //剪枝 1:最优化剪枝 

2:若一个高手会做的题有另一个高手都会做,则这个高手没有用

for(int i=1;i<=w;i++)
for(int j=1;j<i;j++)
{;
if((a[i].sn|a[j].sn)==a[j].sn) {kk[i]=1; break;} //kk表示这个高手有没有用
//剪枝 2:若这个高手会做的题有另一个人全会做,则是废物,去掉没有用的高手 (核心剪枝)
}

3:若有一个题只有一个高手能做,则将这个高手“特招”

for(int i=1;i<=w;i++)
for(int j=1;j<i;j++)
{;
if((a[i].sn|a[j].sn)==a[j].sn) {kk[i]=1; break;} //kk表示这个高手有没有用
//剪枝 2:若这个高手会做的题有另一个人全会做,则是废物,去掉没有用的高手 (核心剪枝)
}

顺序性优化:将做题多的高手排在前面(也许没用)

bool cmp(node x,node y){return x.sum>y.sum;}
sort(a+1,a+n+1,cmp); //顺序性优化: 按会做的题数排序

加上这些优化在top上大概200多ms(第二次AC记录),应该还有其他优化,但因为太ruo想不出来

这题用二进制表示状态好像多此一举

最后,附上完整代码(有点小长)

打注释都打了半个多小时QAQ

代码

#include <bits/stdc++.h>
using namespace std;
int n,w,ans=1e9,tt;//tt记录特招人数
bool k[66],kk[66],vis[66]; //k记录是否“特招”,kk记录有木有用,vis标记访问
struct node{
long long sn,sum;
int p[66];
}a[66]; //p数组记录每个高手能做的题,sn记录状态,sum记录题数,
long long num[66],q[66][66],s[66],ss[66];
//q记录每个题能做的高手,s和ss记录每个题能做的人数(有点重复了),num记录每个题的二进制,
bool cmp(node x,node y){return x.sum>y.sum;}
void find(int x,int ws,long long ssum) //x表示当前题号, ws表示高手数, ssum用二进制存储状态
{
if(ws>=ans) return; //剪枝 1:最优化剪枝
if(x>n) {ans=ws; return;} //结束条件, 更新答案
if(((ssum>>(x-1))&1==1)) {find(x+1,ws,ssum); return;} //如果这题已做,往后搜索
for(int i=1;i<=s[x];i++)
{
if(k[q[x][i]]) continue; //判断这个高手是不是 “特招”的
int tmp=q[x][i];
if(vis[tmp]) continue; //如果这个高手已经用过了
vis[tmp]=1;
find(x+1,ws+1,ssum|a[tmp].sn); //一定要用 | 运算,防止重复
vis[tmp]=0;
}
return;
}
int main()
{
scanf("%d%d",&n,&w);
num[1]=1;
for(int i=2;i<=n;i++) num[i]=num[i-1]<<1; //用2进制来表示状态
for(int i=1;i<=w;i++)
{
int x;
scanf("%d",&x);
for(int j=1;j<=x;j++)
{
int y;
scanf("%d",&y);
s[y]++;
if((a[i].sn|num[y])!=a[i].sn) //注意这里要用 | 运算,判断这题有没有出现过
a[i].sum++,a[i].p[a[i].sum]=y; //存储每个高手能做的题
a[i].sn=a[i].sn|num[y]; //更新状态
}
}
sort(a+1,a+n+1,cmp); //顺序性优化: 按会做的题数排序
for(int i=1;i<=w;i++)
for(int j=1;j<i;j++)
{
// if(i==j||kk[j]) continue;
if((a[i].sn|a[j].sn)==a[j].sn) {kk[i]=1; break;} //kk表示这个高手有没有用
//剪枝 2:若这个高手会做的题有另一个人全会做,则是废物,去掉没有用的高手 (核心剪枝)
}
long long ttmp=0;
for(int i=1;i<=w;i++)
{
if(kk[i]) continue; //直接过滤废物高手
for(int j=1;j<=a[i].sum;j++)
{
int tmp=a[i].p[j];
q[tmp][++ss[tmp]]=i;
}
}
//记录能做每个题的高手
for(int i=1;i<=n;i++)
{
if(s[i]==1&&!k[q[i][1]]) //判断第i题是不是只有一个高手会做
{
tt++;
//printf("%d\n",q[i][1]);
int tmp=q[i][1];
ttmp=ttmp|a[tmp].sn; //ttmp记录初始的搜索状态
k[tmp]=1; //标记这个高手不用考虑
}
} //剪枝 3:若有一个题只有一个高手会做,将他“特招”进来 (这个剪枝收益蛮大的)
find(1,0,ttmp);
printf("%d",ans+tt); //注意不要忘了加上“特招”人数
return 0;
}

Topoi 测验1301, 问题C: 1959: 解题 解题报告的更多相关文章

  1. TOPOI 测验1320, 问题C: 4410: [CF41D]Pawn 解题报告

    题目链接 题目大意 在一个树阵中按一定走法取一些树,使和最大且被 k+1整除 解题思路 类似一个数塔问题 因为最后的结果要被 k+1 整除,所以可以记录到每一个点  对 k+1 取余结果不同的最优解( ...

  2. 洛谷 P4397 [JLOI2014]聪明的燕姿 / TOPOI 测验1315, 问题E: 1935: 聪明的燕姿 解题报告

    题目链接 : 1. 洛谷 2.topoi . 大致题意:输入一个数s,找出所有约数和为s的数 关于一个数的约数和求法: 一个>1的整数可以被分解为多个 质数 的乘方,设数 s = p1k1 *  ...

  3. HDU 3791二叉搜索树解题(解题报告)

    1.题目地址: http://acm.hdu.edu.cn/showproblem.php?pid=3791 2.参考解题 http://blog.csdn.net/u013447865/articl ...

  4. 怎样解题 --解题表(how to solve it)

    <怎样解题> 美.波利亚 下面是来自书中的解题表: 理解题目 第一 理解题目 你必须理解题目 未知量是什么?已知数据是什么?条件是什么? 条件有可能满足吗?条件是否可以确定未适量?或者它不 ...

  5. CodeForces 701C They Are Everywhere (滑动窗口)

    题目链接:http://codeforces.com/problemset/problem/701/C 题意:找到字符串中能包含所有元素的最短字符串长度. 利用“滑动窗口”解题 解题思路: 1. 遍历 ...

  6. 【一天一道LeetCode】#63. Unique Paths II

    一天一道LeetCode (一)题目 Follow up for "Unique Paths": Now consider if some obstacles are added ...

  7. 【一天一道LeetCode】#27. Remove Element

    一天一道LeetCode系列 (一)题目 Given an array and a value, remove all instances of that value in place and ret ...

  8. 【一天一道LeetCode】#26. Remove Duplicates from Sorted Array

    一天一道LeetCode系列 (一)题目 Given a sorted array, remove the duplicates in place such that each element app ...

  9. LeetCode7-ReverseInteger

    LeetCode7-ReverseInteger LeetCodeeasyOverflow 题目 题目所在链接为 LeetCode-7:ReverseInteger 题目描述 给出一个32位的有符号整 ...

随机推荐

  1. IOS 实现banner循环轮播

    在项目中把banner图片UIImageView一张一张的放入UIScrollView中,通过设置UIScrollView的pagingEnabled属性为YES,则可以做到当用户滑动banner时图 ...

  2. 大白话AOP

    工作一年多后, 第二次看了韩顺平老师讲的AOP (11年的Spring 教学视频) AOP还是比较艰涩的东西. 从刚开始 碰Java项目去找书看开始, 到学了拦截器知道AOP就是处理事务, 日志, 安 ...

  3. STL stl_uninitialized.h

    stl_uninitialized.h // Filename: stl_uninitialized.h // Comment By: 凝霜 // E-mail: mdl2009@vip.qq.com ...

  4. IO - 文件的读取与写入

    最近有较多提取文档内容,然后拼接成sql之类的,但是纯粹的复制粘贴又太傻,就写了一个脚本,自动读取文件内容(word文档可能需要复制成txt文本),然后拼接sql,最后写入到指定文件中,试了下还是蛮好 ...

  5. log4net初探

    /// <summary> /// Static constructor that initializes logging by reading /// settings from the ...

  6. JZOJ 1003【东莞市选2007】拦截导弹——dp

    题目:https://jzoj.net/senior/#main/show/1003 只要倒推一下第一次上升的最长和第一次下降的最长就行了.不用n^2logn,枚举了 j 还要用树状数组找值比自己大的 ...

  7. Trie(前缀树/字典树)及其应用

    Trie,又经常叫前缀树,字典树等等.它有很多变种,如后缀树,Radix Tree/Trie,PATRICIA tree,以及bitwise版本的crit-bit tree.当然很多名字的意义其实有交 ...

  8. js中的setInterval

    跟几个例子吧 计时器的例子: /** * Created by Administrator on 2016/8/5. */ (function () { function show() { var t ...

  9. 【opencv学习笔记八】创建TrackBar轨迹条

    createTrackbar这个函数我们以后会经常用到,它创建一个可以调整数值的轨迹条,并将轨迹条附加到指定的窗口上,使用起来很方便.首先大家要记住,它往往会和一个回调函数配合起来使用.先看下他的函数 ...

  10. python编程范式培训文档,主要是结合4种代码和,对oop和面向过程编程区别和oop转化公式培训。

    这是写得培训文档.代码例子在附件. 是经过深入考察 4个git项目里面,找出代码非常非常十分low.代码重复得吓人的的最本质原因.提炼出oop转化公式. 围绕附件中的4种代码写法思维来实现同一个任务, ...