T1 FGoperation
这个题直接做肯定错,考虑dp递推。
Dp[i][j]表示操作前i个数后最终得到j的操作方案数
目标就为dp[n][k]
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
dp[1][a[1]]=1;
for(int i=1;i<n;++i)
{
for(int j=0;j<=9;++j)
{
dp[i+1][(j+a[i+1])%10]=(dp[i][j]+dp[i+1][(j+a[i+1])%10])%mod;
dp[i+1][(j*a[i+1])%10]=(dp[i][j]+dp[i+1][(j*a[i+1])%10])%mod;
}
}
for(int i=0;i<=9;++i) printf("%d\n",dp[n][i]%mod);
T2 Mr.Youngs Picture Permutations
假定安排人的时候是按照从高到矮安排的。则对于任意一排,只要这一排的人数大于等于下面那排,那么这一排可以再加一个人(这一个人比已经排好的人全都矮,按照这个模式选人可以保证完全符合题目要求),以dp【a】【b】【c】【d】【e】表示第一排选好了a人,第二排选好了b个人...,第五排选好了e个人的方案数。
int s[6];
memset(s,0,sizeof(s));
for(int i=1;i<=n;++i) cin>>s[i];
unsigned dp[s[1]+1][s[2]+1][s[3]+1][s[4]+1][s[5]+1];
memset(dp,0,sizeof(dp));
dp[0][0][0][0][0]=1;
for(int a=1;a<=s[1];++a)
for(int b=0;b<=min(a,s[2]);++b)
for(int c=0;c<=min(b,s[3]);++c)
for(int d=0;d<=min(c,s[4]);++d)
for(int e=0;e<=min(d,s[5]);++e)
{
if(a-1>=b && a ) dp[a][b][c][d][e]+=dp[a-1][b][c][d][e];
if(b-1>=c && b ) dp[a][b][c][d][e]+=dp[a][b-1][c][d][e];
if(c-1>=d && c) dp[a][b][c][d][e]+=dp[a][b][c-1][d][e];
if(d-1>=e && d) dp[a][b][c][d][e]+=dp[a][b][c][d-1][e];
if(e) dp[a][b][c][d][e]+=dp[a][b][c][d][e-1];
}
cout<<dp[s[1]][s[2]][s[3]][s[4]][s[5]]<<endl;
T3 IncDec Sequence
第一问就是问让差分数组d[2]~d[n]全部变为0需要几步。
我们可以的操作:1、选一个d[]让它+1,并在后面选一个d[]让它-1
2、选一个d[]让它-1,并在后面选一个d[]让它+1
一个正数d[]和一个负数d[]互相相消才符合最小操作数。
设正数d数值之和为X,负数d数值之和为Y
这一步花费min(X,Y)
然后会剩一些没消掉的的d[],总共abs(X-Y)
让它们跟d[n+1]或者d[1]消即可。所以第一问答案为min(X,Y)+abs(X-Y)==max(X,Y)
第二问相当问d[1]有几种可能?只关心正负相消后剩下来的这些abs(X-Y)到底是跟d[1]消还是跟d[n+1],这些数与d【1】操作才会导致d【1】值改变(这些abs(X-Y)是同号的,要么全部都是操作1,要么全部都是操作2,故d[1]值的改变是单调的),与d[n+1]操作不会导致d[1]改变。会跟d[1]进行操作的要么0个数,要么1个数,要么2个数, ... ,要么abs(X-Y)个数,共abs(X-Y)+1种情况故答案为abs(X-Y)+1
int n,a[100002];
cin>>n;
for(int i=1;i<=n;++i) cin>>a[i];
for(int i=1;i<=n;++i)
{
d[i]=a[i]-a[i-1];
}
int X=0,Y=0;
for(int i=2;i<=n;++i)
{
if(d[i]>0) X+=d[i];
else if(d[i]<0) Y-=d[i];
}
cout<<min(X,Y)+abs(Y-X)<<endl<<abs(X-Y)+1;
return 0;
T4最大字段和
Dp[i]:选i的前i个数最大连续子段和dp[i]=max(dp[i-1]+a[i],a[i])。
Ans=max(dp[1~n])。
T5领地选择
求二维前缀和:for i for j s[i][j]=s[i-1][i]+s[i][j-1]-s[i-1][j-1]
for(int i=1;i<=n;++i)
{
for(int j=1;j<=m;++j)
{
scanf("%lld",&a[i][j]);
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
if(i-c>=0 && j-c>=0)
{
if(ans<s[i][j]+s[i-c][j-c]-s[i-c][j]-s[i][j-c])
{
ans=s[i][j]+s[i-c][j-c]-s[i-c][j]-s[i][j-c];
tx=i-c+1,ty=j-c+1;
}
}
}
}
T5.5二维差分
往x1,y1(左上) x2,y2(右下) +x:d[x1,y1]+=x;d[x2+1,y1]-=x;d[x1,y2+1]-=x d[x2+1,y2+1]+=x.
T6最大加权矩阵
暴力的话n4方,炸。对于每一列,如果可以任意调用这一列第i~j行的和,即可把原矩阵的行数压缩为1,然后对于这唯一的一行求最大连续字段和即可。
压缩:for(int i=1;i<=n;++i)
{
for(int j=1;j<=n;++j)
{
scanf("%d",&a[i][j]);
a[i][j]+=a[i-1][j];
}
}
此时a[i][j]的意义为第j列前i行的前缀和。
int ans=-1000000000;
for(int i=1;i<=n;++i)
{
for(int k=1;k<=i;++k)
{
int dp[MAXN];
memset(dp,0,sizeof(dp));
for(int j=1;j<=n;++j)
{
dp[j]=max(dp[j-1]+a[i][j]-a[i-k][j],a[i][j]-a[i-k][j]);
ans=max(dp[j],ans);
}
}
}
k表示选包括第i行往上的行,一共k行。
T7合唱队形
找最长单调上升子序列。从左往右dp1[i]=max(dp[j]+1,1)【a[j]<a[i]】为前i个数的最长单调上升序列长度,从右往左找dp2[i]。
Ans=min(n-(dp1[i]+dp2[i]-1) )(重复算了自己一次,要-1)。
Dp1的求法:
for(int i=1;i<=n;++i)
{
for(int j=0;j<i;++j)//从0开始,意义为只选自己
{
if(t[i]>t[j])
dp1[i]=max(dp1[j]+1,dp1[i]);
}
}