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(); }