1.算法板块
1.dp状态机模型
状态机的模型其实可以抽象成图论,通过每一步之间不同的选择去到不同的节点,而我们要求的是满足题目条件的节点,通过题目限制要求的是最大/最小使得我们dp转移的时候不必去到所有的节点,从而减少了时间复杂度。
例题1.
要求的是偷的最大价值那么,并且局限了不能连续偷,那么可以考虑dp,dp[i][j]的状态表示是第i件物品偷/不偷时最大收益,状态转移方程很好想。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e5 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
int dp[N][2];
int n,a[N];
void work()
{
cin>>n;
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
dp[i][0] = dp[i][1] = 0;
}
for(int i = 1 ; i <= n ; i++)
{
dp[i][0] = max(dp[i][0] , max(dp[i - 1][0] , dp[i - 1][1]));
dp[i][1] = max(dp[i][1] , dp[i - 1][0] + a[i]);
}
cout<<max(dp[n][0] , dp[n][1])<<"\n";
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
work();
}
}
例题2.
与上题有相同之处,在于在第i天是买还是不买,是卖还是不卖,不同的选择就会指向不同的节点,因此还是dp,我们设计一个三维的dp,表示的状态就是第i天正在进行第j次交易的时候手中无/有货,如果我们还是表示成买/不买,卖/不卖就很难表示状态,通过表示成是否有货物也可以通过转移的时候描述出买/不买,卖/不卖
状态转移方程
手上有货的时候,我们可以由昨天有货直接转移,也可以由昨天无货转移,若昨天无货转移到今天有货就说明买入了ai,并且需要注意的是我们是通过上一次交易转移来的,因为手中买入货物相当于一次交易的开始,而你本身作为第j次交易的起点上一个不同的状态只能是上一次交易的,手上无货的时候可以有昨天无货直接转移,也可以由昨天有货转移,表达的含义就是今天卖出的,同时需要注意初始化。
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 1e5 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
int dp[N][105][2]; //第i天正在进行第j次交易的时候手中无/有货
int n,k;
int a[N];
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>k;
memset(dp , -inf , sizeof dp);
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
dp[i][0][0] = 0;
}
dp[0][0][0] = 0; //注意初始化
for(int i = 1 ; i <= n ; i++)
{
for(int j = 1 ; j <= k ; j++)
{
dp[i][j][1] = max(dp[i - 1][j][1] , dp[i - 1][j - 1][0] - a[i]);
dp[i][j][0] = max(dp[i - 1][j][0] , dp[i - 1][j][1] + a[i]);
}
}
int maxn = 0;
for(int i = 0 ; i <= k ; i++)
{
maxn = max(maxn , dp[n][i][0]);
}
cout<<maxn;
}
2.鸽巢原理
鸽巢原理的含义就是n只鸽子飞进m个巢穴,那么必定至少有一个巢中飞进了floor( (n-1)/m) ) + 1,例如n+1只鸽子飞入n个巢穴那么就说明至少有一个巢穴飞进了两只鸽子
例题1.Problem - 1205 (hdu.edu.cn)
我们找到数量最多的糖果个数maxn,糖果的和为sum,那么我们让maxn为鸟巢,sum - maxn为鸽子,那么我们从sum-maxn中一次取出maxn只鸽子依次进入不同的巢穴,若sum - maxn < maxn - 1的话那么最后两个巢穴没有间隔就失败了,否则都是可行的
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e3 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
//鸽巢原理
void work()
{
int n;
cin>>n;
ll sum = 0,maxn = -1;
for(int i = 1 ; i <= n ; i++)
{
ll x;
cin>>x;
maxn = max(maxn , x);
sum += x;
}
sum -= maxn;
if(sum >= maxn - 1)
{
cout<<"Yes"<<"\n";
}else
{
cout<<"No"<<"\n";
}
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
work();
}
}
2.例题2B-智乃想考一道鸽巢原理_牛客练习赛123 (nowcoder.com)
我们可以发现当一个球的颜色很多的时候(离谱的多),那么只有这个一个球能留下来,其他的都没办法保留,那么一定存在一个界值会发生这种情况,我们假设有一个数组a1,a2,a3,a4......an降序排列,想要留下an那么一定是最大的相消,但也不能直接消完,例如a1,a2相消,我们要在a2 == a3的时候停止,那么我们就发现数组变成了a1-(a2-a3),a3,a3,a4......an,接下来若a1还足够大的话就继续,下一步会变成a1-(a2-a3)-2*(a3-a4),a4,a4,a4,a5....an,整理一下a1-a2-a3+2*a4,a4,a4,a4,a5....an,那么一直消下去就变成a1-a2-a3-......+(n-2)an,an,an......an,如果a1还足够大最后就是消去剩下的所有就变成了a1-a2-a3-...-an,所以我们发现当最大值maxn > sum - maxn的时候除了maxn自己是可能保留的其余的都不能,并且我们还发现当maxn <= sum - maxn的时候除了ai的其他所有球是可以两两相消的,回归到鸽巢原理我们需要找出本题的巢和鸽子,很明显巢穴是球的最大值,鸽子是sum-maxn但是当ai就是maxn的时候怎么办,所以我们还要找出一个maxn2即第二多的球数作为第二巢穴,当sum - maxn - a[i] <= maxn的时候意味着其他球没办法消去最大值,这时候我们需要看ai是否可以补足最大值并且至少还有一个,若sum - maxn - a[i] > maxn的时候说明鸽子多于巢穴,这个时候回归到我们之前推的式子,这说明a1没办法坚持到最后而是在中途就没办法维持了,那么就会出现ax,ax,ax.......,an-y,an-y-1,.....an的情况,这个时候我们继续用之前的办法消到严格倒数第二大的数那么最后要么剩下1要么剩下0,本质就是两两相消,所以ai > ((sum-ai)%2)的时候i就保留,感觉本题结论不复杂但是需要推式子+感觉
#define first x
#define second y
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll , ll> pii;
typedef pair <int , char> pic;
const int N = 2e5 + 10,inf = 0x3f3f3f3f,mod = 1e9 + 7;
ll a[N];
int n;
void work()
{
cin>>n;
if(n == 1)
{
ll x;
cin>>x;
cout<<1<<"\n";
return ;
}
ll sum = 0,maxn1 = 0,maxn2 = 0,mi = 0;
for(int i = 1 ; i <= n ; i++)
{
cin>>a[i];
sum += a[i];
if(maxn1 < a[i])
{
maxn2 = maxn1;
maxn1 = a[i];
}else if(maxn1 >= a[i] && a[i] > maxn2)
{
maxn2 = a[i];
}
}
for(int i = 1 ; i <= n ; i++)
{
ll maxn;
if(a[i] == maxn1)
{
maxn = maxn2;
}else
{
maxn = maxn1;
}
if(sum - maxn - a[i] <= maxn)
{
if(a[i] - (maxn - sum + maxn + a[i]) > 0)
{
cout<<1<<" ";
}else
{
cout<<0<<" ";
}
}else
{
if(a[i] > ((sum - a[i]) % 2))
{
cout<<1<<" ";
}else
{
cout<<0<<" ";
}
}
}
cout<<"\n";
}
int main()
{
std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int t;
cin>>t;
while(t--)
{
work();
}
}
2.web板块
勉强算是了解完了git,学习了部分git命令以及linux的命令,明天开始还是继续学习css顺便复习html,这几天web学习怠惰。