2022.9.20测试
一测330,原因T4是乱打else导致炸了。
T1:P2327 [SCOI2005]扫雷(黄)
T2:P1896 [SCOI2005] 互不侵犯(蓝)
T3:P2330 [SCOI2005]繁忙的都市(黄)
T4:2331 [SCOI2005]最大子矩阵(蓝)
T1:
记录 \(dp_{i,j,k}\) 代表第 \(i\) 个未知格子,雷的状态为 \(j\),上一个雷的状态为 \(k\) 的方案数。
那么要满足:
\(a_{i-1}=j+k+l\hspace{0.2cm}\text{其中}\hspace{0.1cm}l\hspace{0.1cm} \text{为第}\hspace{0.1cm}i-2\hspace{0.1cm}\text{个未知格子上的雷情况}\text{,且:}a_i\ge j+k\)
那么就可以转移了
\(dp_{i,j,k}+=dp_{i-1,k,l}\)
最后统计 \(\sum_{j=0}^{1}\sum_{k=0}^1dp_{n,j,k}\) 即可。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=1e4+5;
int n,a[N],dp[N][2][2];
signed main()
{
freopen("sweeper.in","r",stdin);
freopen("sweeper.out","w",stdout);
scanf("%lld",&n);
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
if(a[1]||a[2])dp[1][1][0]=1;
dp[1][0][0]=1;
for(int i=2;i<=n;i++)
{
for(int j=0;j<2;j++)
{
for(int k=0;k<2;k++)
{
if(j+k>a[i])break;
for(int l=0;l<2;l++)
{
if(j+k+l!=a[i-1])continue;
dp[i][j][k]+=dp[i-1][k][l];
}
}
}
}
int ans=0;
for(int i=0;i<2;i++)for(int j=0;j<2;j++)if(i+j==a[n])ans+=dp[n][i][j];
printf("%lld",ans);
return 0;
}
T2:
经典状压模板,不讲了。
设 \(dp_{i,q,j}\) 代表第 \(i\) 行,已放 \(q\) 个棋子,第 \(i\) 行状态为 \(j\) 的方案数。
首先同行国王不能互相攻击,所以 \(j \land (j<<1)=j \land (j<<1)=0\)
另外不同行的国王也不能互相攻击,设上一行的状态为 \(t\),所以 \(j \land t=j\land(t<<1)=j\land(t>>1)=0\)
先预处理出来合法的 \(j\),并用lowbit处理出来 \(num_j\),代表每个状态 \(j\) 有多少个国王。
最后进行dp转移。
\(dp_{i,q,j}+=dp_{i-1,q-num_j,t}\)
然后答案为 \(\sum_{i=1}^{(1<<n)-1}dp_{n,i,k}\)。
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=9;
int n,k,dp[N+1][N*N+1][1<<N],p[1<<N],vis[1<<N];
inline int lowbit(int x)
{
return x&(-x);
}
void pre()
{
for(int i=0;i<1<<n;i++)
{
if((i&(i<<1))||(i&(i>>1)))continue;
vis[i]=1;
int x=i;
while(x)x-=lowbit(x),p[i]++;
}
}
signed main()
{
freopen("king.in","r",stdin);
freopen("king.out","w",stdout);
scanf("%lld%lld",&n,&k);
pre();
for(int i=0;i<1<<n;i++)if(vis[i])dp[1][p[i]][i]=1;
for(int i=2;i<=n;i++)
{
for(int j=0;j<1<<n;j++)
{
if(!vis[j])continue;
for(int t=0;t<1<<n;t++)
{
if(!vis[t])continue;
if((j&t)||(j&(t<<1))||(j&(t>>1)))continue;
for(int q=p[j]+p[t];q<=k;q++)dp[i][q][j]+=dp[i-1][q-p[j]][t];
}
}
}
int ans=0;
for(int i=0;i<1<<n;i++)ans+=dp[n][k][i];
printf("%lld",ans);
return 0;
}
T3:
题看完以后就是个最小生成树,然后就没了。
要求要所有点连通,且边最少,所以取 \(n-1\) 条边。
然后要求取出的所有边的权值最大值最小,那么每次贪心取最小边,如果这个边连接的两个点已在一个子图,那么不能选取这条边。可以利用并查集判断两个点是否在一个子图。
实质上就是做最小生成树的工作。
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=1e5+5;
int n,m,f[N];
struct node
{
int from,to,data;
}a[N];
bool cmp(node fi,node se)
{
return fi.data<se.data;
}
int afind(int x)
{
if(x==f[x])return x;
return f[x]=afind(f[x]);
}
int krus()
{
sort(a+1,a+1+m,cmp);
int ans=0;
for(int i=1;i<=m;i++)
{
if(afind(a[i].from)==afind(a[i].to))continue;
f[afind(a[i].from)]=afind(a[i].to);
ans=a[i].data;
}
return ans;
}
int main()
{
freopen("city.in","r",stdin);
freopen("city.out","w",stdout);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)f[i]=i;
for(int i=1;i<=m;i++)scanf("%d%d%d",&a[i].from,&a[i].to,&a[i].data);
int ans=krus();
printf("%d %d",n-1,ans);
return 0;
}
T4:
根据数据范围 \(m\le 2\) 下手。
1.当 \(m=1\)
k个最大字段和问题。
记录 \(dp_{i,j,k}\) 为枚举到第 \(i\) 个数,已用掉 \(j\) 个子段,第 \(i\) 个数选择情况为 \(k\) 得到的最大值。
当 \(k=0\) 时,只能从上一层转移过来,上一层可选可不选,所以
\(dp_{i,j,k}=\max(dp_{i-1,j,0},dp_{i-1,j,1})\)
当 \(k=1\) 时,当选上一个数时,上个数可以与这个数合并为一个子序列,也可以这个数单独成一个子序列,上一个数不选那这个数单独成子序列。
\(dp_{i,j,k}=a_i+\max(dp_{i-1,j-1,0},dp_{i-1,j,1},dp_{i-1,j-1,1})\)
2.当 \(m=2\)
这个时候对于每一层的数,状态变多了起来,这里选择穷举所有状态。
设状态为 \(j\)。
当 \(j=0\) 时,表示这一层左右两数都不选。
当 \(j=1\) 时,表示这一层只选左边的数。
当 \(j=2\) 时,表示这一层只选右边的数。
当 \(j=3\) 时,表示这一层选左右两边的数,且在同一个子矩阵。
当 \(j=4\) 时,表示这一层选左右两边的数,但不在同一个子矩阵。
那又可以列dp方程了,设 \(dp_{i,j,p}\) 为第 \(i\) 层,状态为 \(j\),且已选择 \(p\) 个子矩阵后得到的最大值。
当此层状态 \(j\) 与上层状态 \(t\) 相同时,可以之间从上一层转移过来,矩阵数不变。
当 \(j=t=0\) 时,\(dp_{i,j,p}=dp_{i-1,j,p}\)
当 \(j=t=1\) 时,\(dp_{i,j,p}=a_{i,0}+dp_{i-1,j,p}\)
当 \(j=t=2\) 时,\(dp_{i,j,p}=a_{i,1}+dp_{i-1,j,p}\)
当 \(j=t=3\) 时,\(dp_{i,j,p}=a_{i,0}+a_{i,1}+dp_{i-1,j,p}\)
当 \(j=t=4\) 时,\(dp_{i,j,p}=a_{i,0}+a_{i,1}+dp_{i-1,j,p}\)
若此层状态与上层状态不同,那么矩阵数目会随之改变。
当 \(j=0,j \ne t\) 时,此时矩阵数目与上一层一样
\(dp_{i,j,p}=dp_{i-1,t,p}\)
当 \(j=1,j\ne t\) 时,此时从上层转移下来,那么这将是下一个子矩阵的起点,方案数 \(+1\)。
\(dp_{i,j,p}=a_{i,0}+dp_{i-1,t,p-1}\)
特殊情况:当 \(t=4\) 时,那么上一层的左边是一个单独的字矩阵,可以选择与上一层进行拼接。
\(dp_{i,j,p}=a_{i,0}+dp_{i-1,t,p}\)
当 \(j=2,j\ne t\) 时与第上种情况相同。
\(dp_{i,j,p}=a_{i,1}+dp_{i-1,t,p-1}\)
特殊情况:当 \(t=4\) 时
\(dp_{i,j,p}=a_{i,1}+dp_{i-1,t,p}\)
当 \(j=3,j\ne t\) 时,无论上一层情况如何,也不能与上一层进行衔接。所以
\(dp_{i,j,p}=a_{i,0}+a_{i,1}+dp_{i-1,t,p-1}\)
当 \(j=4,j\ne t\) 时,若上一层为 \(1\) 或 \(2\) 那么就有一列可以衔接上一层,若无法衔接或不衔接方案数得加 \(2\)。
\(dp_{i,j,p}=a_{i,0}+a_{i,1}+dp_{i,t,p-2}\)
当特殊情况 \(t=1\) 或 \(t=2\) 时
\(dp_{i,j,p}=a_{i,0}+a_{i,1}+dp_{i,t,p-2}\)
注意:上述所有的特殊情况并不代表只从特殊情况转移,而是要与一般情况做比较取最优解。
然后求出 \(\max(dp_{n,j,k})\) 即可。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=105;
const int K=15;
int n,m,k,a[2][N],dp[N][5][K],f[N][K][2];
int main()
{
freopen("matrix.in","r",stdin);
freopen("matrix.out","w",stdout);
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++)for(int j=0;j<m;j++)scanf("%d",&a[j][i]);
if(m==1)
{
memset(f,-0x3f,sizeof(f));
f[0][0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<=k;j++)
{
f[i][j][0]=max(f[i-1][j][0],f[i-1][j][1]);
if(j>0)f[i][j][1]=max(f[i-1][j-1][0]+a[0][i],max(f[i-1][j-1][1]+a[0][i],f[i-1][j][1]+a[0][i]));
}
}
int ans=max(f[n][k][0],f[n][k][1]);
printf("%d",ans);
return 0;
}
memset(dp,-0x3f,sizeof(dp));
dp[0][0][0]=0;
for(int i=1;i<=n;i++)
{
for(int j=0;j<5;j++)
{
for(int t=0;t<5;t++)
{
for(int p=0;p<=k;p++)
{
if(j!=t&&p>0)
{
if(j==1)dp[i][j][p]=max(dp[i][j][p],a[0][i]+dp[i-1][t][p-1]);
if(j==1&&t==4)dp[i][j][p]=max(dp[i][j][p],a[0][i]+dp[i-1][t][p]);
if(j==2)dp[i][j][p]=max(dp[i][j][p],a[1][i]+dp[i-1][t][p-1]);
if(j==2&&t==4)dp[i][j][p]=max(dp[i][j][p],a[1][i]+dp[i-1][t][p]);
if(j==3)dp[i][j][p]=max(dp[i][j][p],a[0][i]+a[1][i]+dp[i-1][t][p-1]);
if(j==0)dp[i][j][p]=max(dp[i][j][p],dp[i-1][t][p]);
if(j==4)
{
if(p>=2)dp[i][j][p]=max(dp[i][j][p],dp[i-1][t][p-2]+a[0][i]+a[1][i]);
if(t==2||t==1)dp[i][j][p]=max(dp[i][j][p],dp[i-1][t][p-1]+a[0][i]+a[1][i]);
}
}
if(j==t)
{
if(j==0)dp[i][j][p]=max(dp[i][j][p],dp[i-1][t][p]);
if(j==1)dp[i][j][p]=max(dp[i][j][p],a[0][i]+dp[i-1][t][p]);
if(j==2)dp[i][j][p]=max(dp[i][j][p],a[1][i]+dp[i-1][t][p]);
if(j==3)dp[i][j][p]=max(dp[i][j][p],a[0][i]+a[1][i]+dp[i-1][t][p]);
if(j==4)dp[i][j][p]=max(dp[i][j][p],a[0][i]+a[1][i]+dp[i-1][t][p]);
}
}
}
}
}
int ans=-1e9;
for(int i=0;i<5;i++)ans=max(ans,dp[n][i][k]);
printf("%d",ans);
return 0;
}
/*
3 2 1
-1 2
2 -3
3 1
*/

浙公网安备 33010602011771号