一直调不对代码
不光是考试的时候
晚上也是
很烦
心态爆炸
跟T3杠了四个钟头
唉。。。
T1
原题戳这里–>HDU 1176(要是我也有这种运气。。。)
这题其实跟数字三角形很像(没错就是那道dp入门题)
用时间t来分层,每一次t++都会对原来的点最左边和最右边分别加一个可能的位置
然后就变成了数字三角形了对吧
所以我们就可以得出状态转移方程:
f[i][j]+=max(f[i-1][j],max(f[i-1][j-1],f[i-1][j+1]))
这里需要注意的是,当j=0或j=10时,不能再往左走或往右走(因为馅饼只能落在0-10的范围内)
题目简单,不贴代码
T2
原题戳这里–>cf 474D
这题可以通过观察得出状态转移方程(其实就是递推式)
就是某道走楼梯的题
只是走楼梯一次可以走一格或两格
这题是一次吃一或k个
所以方程是:
f[i]=f[i-1]+f[i-k]
f[i]是当有i个蛋糕时的方法数
那么怎么求x1-x2的方法数和呢
求一个前缀数组就好啦
由于要取模,所以我在取模的时候只对f数组取了模,为了防止最后减法的时候得到负数
或者可以在取模的时候先加上模数
不贴代码
T3
题目戳这里–>cf 366C
这道题目说起来就气
考试的时候我非常轻松地想到就是0/1背包
然后迅速敲了一个dfs暴搜
然后就跟n<=100的数据杠上了
先想了记忆化
发现并不能过第六组数据
然后发现我的记忆化删多了(因为我记忆化的是a和b的累加和,可能会有suma,sumb重复,而走到这一种情况的方法不同)
然后就开始疯狂想剪枝
然后。。。
不是剪多了WA就是减少了TLE(我*)
最后罚时30次。。。
所以正经的方法是:
读入的时候预处理,将每个水果的能量值储存为美味程度减去k倍的能量值
那么在做0/1背包的时候只要输出f[0]的值就行了
接下来有一个问题
f数组的下标没有负的怎么办
所以可以用强大的偏移!!!
对于每个i加上100000就可以啦
对了
当b[i]>=0时
我们需要反着搜
而b[i]<0时
就得正着搜(就是从小往大搜)
贴一下代码
include<bits/stdc++.h>
using namespace std;
int a[101];
int n,k;
int f[400001];
int b[101];
int y;
int main()
{
scanf("%d%d",&n,&k);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
for (int i=1;i<=n;i++)
{
scanf("%d",&y);
b[i]=a[i]-y*k;
}
memset(f,-1,sizeof(f));
f[100000]=0;//由于偏移,f[0]就是现在的f[100000]
for (int i=1;i<=n;i++)
{
if (b[i]>=0)//正搜与反搜
{
for (int j=200000;j>=0;j--)
if (f[j]!=-1 && j+b[i]<=200000)
f[j+b[i]]=max(f[j]+a[i],f[j+b[i]]);
}
else
{
for (int j=0;j<200000;j++)
if (f[j]!=-1 && j+b[i]>=0)
f[j+b[i]]=max(f[j]+a[i],f[j+b[i]]);
}
}
if (f[100000]==0)
f[100000]=-1;
cout <<f[100000] <<endl;
return 0;
}
T4
题目戳这里–>cf 149D
这题的话。。。
状态转移的限制条件有点多
f数组设四维,分别表示左括号的位置,右括号的位置,左括号染色和右括号染色
当l+1==r的时候
f[l][r][0][1]=1;
f[l][r][1][0]=1;
f[l][r][2][0]=1;
f[l][r][0][2]=1;
//0表示不染色,1表示染红色,2表示染蓝色
当l和r是一对的时候
f[l][r][i][j]+=f[l+1][r-1][x][y];
//x!=i y!=j i,j∈[0,2] i!=j i和j必须只有一个为0
当有并列的括号时
f[l][r][i][j]+=f[l][l的配对][i][x]*f[l的配对+1][r][y][j]
//x,y不能同时=1或2
其实思路也还行,只是限制条件太多了。。。
贴代码
include<bits/stdc++.h>
using namespace std;
const long long p=1000000007;
long long f[701][701][3][3]={};
int match[701];
int q[701];
string s;
int tail=0;
void dfs(int l,int r)
{
if (l+1==r)
{
f[l][r][1][0]=1;
f[l][r][0][1]=1;
f[l][r][2][0]=1;
f[l][r][0][2]=1;
}
else if (match[l]==r)
{
dfs(l+1,r-1);
for (int x=0;x<=2;x++)
for (int y=0;y<=2;y++)
{
if (y!=1)
{
f[l][r][0][1]+=f[l+1][r-1][x][y];
f[l][r][0][1]%=p;
}
if (x!=1)
{
f[l][r][1][0]+=f[l+1][r-1][x][y];
f[l][r][1][0]%=p;
}
if (y!=2)
{
f[l][r][0][2]+=f[l+1][r-1][x][y];
f[l][r][0][2]%=p;
}
if (x!=2)
{
f[l][r][2][0]+=f[l+1][r-1][x][y];
f[l][r][2][0]%=p;
}
}
}
else
{
dfs(l,match[l]);
dfs(match[l]+1,r);
for (int i=0;i<=2;i++)
for (int x=0;x<=2;x++)
for (int y=0;y<=2;y++)
for (int j=0;j<=2;j++)
if (!((x==1&&y==1) || (x==2&&y==2)))
{
f[l][r][i][j]+=f[l][match[l]][i][x]*f[match[l]+1][r][y][j];
f[l][r][i][j]%=p;
}
}
}
void check_()
{
for (int i=0;i<=s.size()-1;i++)
{
if (s[i]=='(')
{
q[++tail]=i+1;
}
if (s[i]==')')
{
match[q[tail]]=i+1;
tail--;
}
}
}
int main()
{
cin >>s;
check_();
dfs(1,s.size());
long long ans=0;
for (int i=0;i<=2;i++)
for (int j=0;j<=2;j++)
{
ans+=f[1][s.size()][i][j];
ans=ans%p;
}
cout <<ans <<endl;
return 0;
}
T5
留白
T6
题目戳这里–>cf 432D
KMP来啦哈哈哈哈哈哈哈
为了让我们深入理解next数组的含义
嗯
所以有了这道题
这道题的一半昨天刚刚由lzydalao讲过
就是求一个字符串的子串在整个字符串中出现过几次
KMP的正常套路
然后另一半呢
通过观察我们可以发现(无敌的观察法)(不知道怎么证明)
满足题目条件的子串就是next[next[next[…next[len]]]](就是while循环不停往下直到next[i]为0)
所以while循环可以很容易解决这个问题
其实还是像递推
贴代码
#include<bits/stdc++.h>
using namespace std;
int next[100001];
char s[100001];
int j;
int num[100001];
int a[100001];
int ans[100001];
int k;
int numm=0;
int main()
{
scanf("%s",s+1);
k=strlen(s+1);
j=0;
next[0]=0;
for (int i=2;i<=k;i++)
{
while (j>0 && s[j+1]!=s[i])
j=next[j];
if (s[j+1]==s[i])
j++;
next[i]=j;
}//处理出next数组
for (int i=k;i>=1;i--)
{
ans[i]++;
ans[next[i]]+=ans[i];
}//求出每一个前缀出现的次数
a[++numm]=k;
while (k!=0)
{
a[++numm]=next[k];
k=next[k];
}//while循环求出满足条件的前缀存在a中
cout <<numm-1 <<endl;
for (int i=numm-1;i>=1;i--)
cout <<a[i] <<" " <<ans[a[i]] <<endl;
return 0;
}
这题主要就是看对next数组的理解(超强的next数组)
T7
留白
T8
留白