5.24 测试总结

T1

景区路线规划

暴力不难想到,直接莽就可了

注意,dfs不要写全局变量

考试的时候因为这个挂了QAQ

55pts的code就不放了,太丑了

正解应该是dp或者记忆化搜索

然后,我还没改出来QAQ

好吧,改完了

他们说,这个记忆化搜索也是dpQAQ

设\(dp[i][j][0,1]\)分别表示男和女,在到第i个点,剩余体力为j时当前开心度的期望,然后我懒,用了个结构体,只写了一个dfs

dfs一开始的时候,先判断这个状态之前是否到达过,如果之前到达过,直接返回这个值就可以了

然后其他的就跟普通dfs差不多了,直接放代码吧

Code:

#include<cstdio>
#define MAX 101
#define re register
namespace OMA
{
   struct node
   {
     int next;
     int to;
     int w;
   }edge[MAX*MAX];
   struct data
   { double p1,p2; };
   int cnt=1,head[MAX];
   double ans1,ans2;
   double dp[MAX][MAX*7][2];
   int c[MAX],h1[MAX],h2[MAX];
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   inline void add(int u,int v,int w)
   {
     edge[++cnt].next = head[u];
     edge[cnt].to = v;
     edge[cnt].w = w;
     head[u] = cnt;
   }
   inline data dfs(int u,int k)
   {
     if(dp[u][k][0]||dp[u][k][1])
     { return (data){dp[u][k][0],dp[u][k][1]}; }
     data ans;
     ans.p1=h1[u],ans.p2=h2[u];
     int du=0,to[MAX],w[MAX];
     for(re int i=head[u]; i; i=edge[i].next)
     {
       int v=edge[i].to;
       int t=edge[i].w;
       if(k-t-c[v]>=0)
       { to[++du]=v,w[du]=t; }
     }
     if(!du)
     { return ans; }
     for(re int i=1; i<=du; i++)
     {
       data temp=dfs(to[i],k-w[i]-c[to[i]]);
       ans.p1+=temp.p1/du,ans.p2+=temp.p2/du;
     }
     dp[u][k][0]=ans.p1,dp[u][k][1]=ans.p2;
     return ans;
   }
   signed main()
   {
     int n=read(),m=read(),k=read();
     for(re int i=1; i<=n; i++)
     { c[i]=read(),h1[i]=read(),h2[i]=read(); }
     for(re int i=1; i<=m; i++)
     {
       int u=read(),v=read(),w=read();
       add(u,v,w),add(v,u,w);
     }
     for(re int i=1; i<=n; i++)
     {
       data temp=dfs(i,k-c[i]);
       ans1+=(double)temp.p1,ans2+=(double)temp.p2;
     }
     printf("%.5lf %.5lf\n",ans1/n,ans2/n);
     return 0;
   }
}
signed main()
{ return OMA::main(); }

T2


考试的时候,想了状压,能拿部分分,又去想了想高斯消元解异或方程组,结果时间不够,都没码QAQ

现在打树形dp

设\(dp1[i][0,1]\)表示按了第i个灯的开关后,达到该状态所按的最小次数,0表示没亮,1表示亮了

\(dp2[i][0,1]\)表示没按第i个灯的开关,达到该状态所按的最小次数,0表示没亮,1表示亮了

设当前节点为\(u\),其儿子节点为\(v\)

初始化

\(dp1[u][1]=1,dp2[u][0]=0\)

\(dp1[u][0]=dp2[u][1]=n+1\)
其实赋成一个不太大的极值就好

在状态转移的过程中\(u\)的四个状态会发生改变,而我又要通过\(u\)改变前的四个状态,推出转移后的四个状态,所以设四个临时变量\(a,b,c,d\)
\(a=dp1[u][0],b=dp1[u][1],c=dp2[u][0],d=dp2[u][1]\)

则有

\[dp1[u][0]=\min\left(b+dp1[v][0],a+dp2[v][0]\right) \]

\[dp1[u][1]=\min\left(a+dp1[v][0],b+dp2[v][0]]\right) \]

\[dp2[u][0]=\min\left(d+dp1[v][1],c+dp2[v][1]\right) \]

\[dp2[u][1]=\min\left(c+dp1[v][1],d+dp2[v][1]\right) \]

还是挺好理解的

最后答案
\(ans=\min\left(dp1[1][1],dp2[1][1]\right)\)

Code:

#include<cstdio>
#include<cstring>
#define MAX 101
#define re register
namespace OMA
{
   int n;
   struct node
   {
     int next;
     int to;
   }edge[MAX*MAX];
   int cnt=1,head[MAX];
   int dp1[MAX][2],dp2[MAX][2];
   inline int read()
   {
     int s=0,w=1; char ch=getchar();
     while(ch<'0'||ch>'9'){ if(ch=='-')w=-1; ch=getchar(); }
     while(ch>='0'&&ch<='9'){ s=s*10+ch-'0'; ch=getchar(); }
     return s*w;
   }
   inline void add(int u,int v)
   {
     edge[++cnt].next = head[u];
     edge[cnt].to = v;
     head[u] = cnt;
   }
   inline void begin()
   {
     cnt=1;
     for(re int i=1; i<=n; i++)
     { head[i] = 0; }
   }
   inline int min(int a,int b)
   { return a<b?a:b; }
   inline void DP(int u,int fa)
   {
     dp1[u][1]=1,dp2[u][0]=0;
     dp1[u][0]=dp2[u][1]=n+1;
     for(re int i=head[u]; i; i=edge[i].next)
     {
       int v=edge[i].to;
       if(v!=fa)
       {
         DP(v,u);
         int a=dp1[u][0],b=dp1[u][1],c=dp2[u][0],d=dp2[u][1];
         dp1[u][0]=min(b+dp1[v][0],a+dp2[v][0]);
         dp1[u][1]=min(a+dp1[v][0],b+dp2[v][0]);
         dp2[u][0]=min(d+dp1[v][1],c+dp2[v][1]);
         dp2[u][1]=min(c+dp1[v][1],d+dp2[v][1]);
       }
     }
   }
   signed main()
   {
     for(re int t=1; t; t++)
     {
       n=read();
       if(!n)
       { break; }
       begin();
       for(re int i=1; i<=n-1; i++)
       {
         int u=read(),v=read();
         add(u,v),add(v,u);
       }
       DP(1,0);
       printf("%d\n",min(dp1[1][1],dp2[1][1]));
     }
     return 0;
   }
}
signed main()
{ return OMA::main(); }

T3

奇怪的道路

我看不出来是状压的状压。

好吧,其实看到k的范围应该去往状压方面想的。

然后,题目中说“任何一个城市都与恰好偶数条道路相连(0也被认为是偶数)”。

所以,奇偶,两种状态可以用0,1来表示,那就妥妥的状压了。

设\(dp_{i,j,sta}\)表示当前已经考虑了i座城市,j条道路,当前状态为sta的方案数。

用0表示奇数,1表示偶数,为了防止转移时出现问题,所以只转移第i个城市的前k个城市,通过异或能够将连边的两个城市由奇变偶,由偶变奇。

则有

\[dp_{i,j,sta}=\sum_{i-k\le l<i}^{0\le sta< 2^{k+1}}dp_{i,j-1,sta^{\wedge}1^{\wedge}2^{i-l}} \]

因为在转移的时候,sta表示的范围在发生变化,所以对于每一个i都要再单独处理一下

\[dp_{i+1,j,sta\times2}=\sum_{0\le j\le m}^{0\le sta<2^{k}}dp_{i,j,sta} \]

Code:

#include<cstdio>
#define top 10
#define MAX 32
#define re register
namespace OMA
{
   int n,m,k;
   int dp[MAX][MAX][1<<top];
   const int p=1000000007;
   inline int max(int a,int b)
   { return a>b?a:b; }
   signed main()
   {
     scanf("%d%d%d",&n,&m,&k);
     dp[2][0][0] = 1;
     for(re int i=2; i<=n; i++)
     {
       for(re int l=max(i-k,1); l<=i-1; l++)
       {
         for(re int j=1; j<=m; j++)
         {
           for(re int temp=0; temp<(1<<k+1); temp++)
           { dp[i][j][temp] = (dp[i][j][temp]+dp[i][j-1][temp^1^(1<<(i-l))])%p;  }
         }
       }
       for(re int j=0; j<=m; j++)
       {
         for(re int temp=0; temp<(1<<k); temp++)
         { dp[i+1][j][temp<<1] = (dp[i][j][temp]+dp[i+1][j][temp<<1])%p;  }
       }
     }
     printf("%d\n",dp[n][m][0]);
     return 0;
   }
}
signed main()
{ return OMA::main(); }
上一篇:2021.06.02模拟赛DP2


下一篇:【剑指 Offer 66. 构建乘积数组 中等】