目录
1.发现环
2.倍数问题
3.质数拆分
4.日志统计
解题思路:本题是判断环的问题,但该题又是无向图,那么我们可以用修改过的拓扑排序来做,把入度改为1,让入度为1的点入队,并用一个vis数组来判断该点是否访问过。
#include
using namespace std;
const int N=200010;
int h[N],e[N],ne[N],idx;
int n;
int q[N],d[N];//q表示队列,d表示点的度数
bool vis[N];
void add(int a,int b)//给图建立边
{
e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}
void topo(int n)
{
int hh=0,tt=-1;
for(int i=1;i<=n;i++)
{ //遍历每一个节点, 入度为1则入队
if(d[i]==1)
{
q[++tt]=i;
vis[i]=true;
}
}
while(hh<=tt)
{
int t=q[hh++];
//遍历头节点的每一个出边,把一些入度为1的判断一下
for(int i=h[t];i!=-1;i=ne[i])
{
int j=e[i];
d[j]--;
//如果结点j入度为1则入队
if(d[j]==1)
{
vis[j]=true;
q[++tt]=j;
}
}
}
}
int main()
{
cin>>n;
memset(h,-1,sizeof h);
for(int i=0;i
解题思路:
首先看这个题,从n个数中选三个数,且三个数相加的和是k的倍数,且满足倍数中最大,那么这一看就是背包空间3,选数满足特殊要求,的01背包吧。因为k只有1e3的数据量,因此我们可以从k入手。我们先按照每个数对k取余的数字将每个数字分类。而对k取余就相当于是对k取模
但是在C++ 的取模正数是正数,负数是负数,而真正意义上的取模符号结果都是正的比如 -1 % 2 用C++的结果是-1而实际真正的结果是1,(数学上模的结果都是正的),所以会看到 (____ % k + k ) % k 解决上面的问题 比如-5 % 3 = 1; 而C++中-5 % 3 的结果是 -2 ,我们通过 ((-5 % 3) + 3) % 3 = 1 ;这样就得到真正的结果。
关于为什么是 (k-x%m+m)%m?
同余模定理:(a+b)%c=(a%c+b%c)%c;
首先(x+m)%m是对x+m取模,因为取模结果只能为非负数;
回到刚才的问题,现在我们要找出t,使得(i+j+t)%m=0,那么t%m=(0-(i+j)%m+m)%m,而通过推论我们发现满足条件时(i+j+t)=0、m或2*m,也就是说这个0实际上换成m或2m都没问题(已验证AC)。
这个明白了,后面dp那里的(k-x%m+m)%m也就迎刃而解,就是求出所有余数情况下最大的值。
#include
#include
#include
#include
using namespace std;
const int N=1e5+10,M=1e3+10;
vector a[M];//vector a[M]这是个二维数组,vector,可以看作第一维,M是第二维度,就是有M个vector。M代表个数
int f[4][N];//4代表当前选的数,N代表余数 ,该集合表示前i 个数中任意选,最多选 j 个且总和是 k 的倍数的最大总和
int get_id(int x,int n)//求余数的函数
{
return (x%n+n)%n;
}
int main()
{
int n,k;
cin>>n>>k;//读入n个数,k的倍数
for(int i=1;i<=n;i++)
{
int t;
cin>>t;
a[t%k].push_back(t);//将数按余数分组
}
memset(f,-0x3f,sizeof f);//一开始是从前0个开始选所以应该初始化为负的无穷大
f[0][0]=0;// 只有 选 0 个 且 体积 为 0 才合法
for(int i=0;i=1;j--)//j代表已经选了多少个数
for(int m=0;m
解题思路:这题就是01背包,先用线性筛把2019前的质数筛出来,然后再用dp求出2019可以拆成的方法数。
#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 2030;
bool st[N];
int primes[N],cnt;
LL f[N]; //f[i][j]表示在前i个质数中能够构成和为j的方案个数
//线性筛把质数搞出来
void get_primes(int n)
{
for(int i = 2;i <= n;i++)
{
//如果是质数,就把加到数组中去
if(!st[i]) primes[cnt++] = i;
//从小到大枚举所有的质数
for(int j = 0; primes[j] <= n/i;j++)
{
st[primes[j] * i] = true;
if(i%primes[j]==0)break;
}
}
}
int main()
{
//用筛法先把2-2019的所有质数都筛出来
get_primes(2019);
//初始化
f[0] = 1;
for(int i =0; i =primes[i];j--)
{
f[j] += f[j-primes[i]];
}
cout << f[2019] << endl;
return 0;
}
题解思路:这道题运用双指针算法,先把每个获赞的帖子存起来,然后再判断在时间差d里面是否获得至少k个赞,满足则是热帖
#include
#include
#include
using namespace std;
const int N=100010;
typedef pairPII;
PII a[N];用来存日志的时间和id
bool st[N];//用来标记帖子的id号
int cnt[N];//用来记录一个id号获得的赞数
int main()
{
int n,d,k;
cin>>n>>d>>k;
for(int i=0;i=d)//超出时间不符合则删去不符合的日志
{
cnt[a[j].second]--;
j++;
}
if(cnt[id]>=k)st[id]=true;
}
for(int i=0;i<=100000;i++)
{
if(st[i])cout<