题目:ACM-ICPC 2018 焦作赛区网络预赛B---Mathematical Curse
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define sl(_) scanf("%lld",&_)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const ll inf=1e18+10;
const double pi=acos(-1.0);
const int n_max=1e3+10;
ll dp[n_max][10],tp[n_max][10],a[n_max];
char s[10];
int main()
{
//freopen("in.txt","r",stdin);
int T; si(T);
while(T--)
{
int n,m,k; siii(n,m,k);
For(i,1,n) sl(a[i]);
ss(s+1);
For(i,1,m)
{
dp[i-1][i]=-inf;
tp[i-1][i]=inf;
}
For(i,0,n) dp[i][0]=tp[i][0]=k;
For(j,1,m)
{
For(i,j,n)
{
dp[i][j]=dp[i-1][j];
tp[i][j]=tp[i-1][j];
ll x=dp[i-1][j-1],y=tp[i-1][j-1];
if(s[j]=='+')
{
x+=a[i];
y+=a[i];
}
else if(s[j]=='-')
{
x-=a[i];
y-=a[i];
}
else if(s[j]=='*')
{
x*=a[i];
y*=a[i];
}
else
{
x/=a[i];
y/=a[i];
}
if(x<y) swap(x,y);
dp[i][j]=max(dp[i][j],x);
tp[i][j]=min(tp[i][j],y);
}
}
printf("%lld\n",dp[n][m]);
}
return 0;
}
一、LCS(最长公共子序列)
二、LIS(最长上升子序列)
#include <bits/stdc++.h>
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Fov(i,x,y) for(int i=(x);i>=(y);--i)
#define Fo(i,x,y) for(int i=(x);i<(y);++i)
#define midf(a,b) ((a)+(b)>>1)
#define L(_) (_)<<1
#define R(_) ((_)<<1)|1
#define fi first
#define se second
#define ss(_) scanf("%s",_)
#define si(_) scanf("%d",&_)
#define sii(x,y) scanf("%d%d",&x,&y)
#define siii(x,y,z) scanf("%d%d%d",&x,&y,&z)
#define mem(x,y) memset(x,y,sizeof(x))
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const double pi=acos(-1.0);
const int n_max=1e4+5;
ll dp[n_max];
int d[25];
int main()
{
//freopen("in.txt","r",stdin);
int T; si(T);
d[0]=1;
For(i,1,20) d[i]=d[i-1]<<1;
while(T--)
{
int n,q,v,c,s; sii(n,q);
mem(dp,0);
dp[0]=1;
For(i,1,n)
{
sii(v,c);
Fo(j,0,c)
{
int x=d[j]*v;
for(int k=10000;k-x>=0;--k) dp[k]=(dp[k-x]+dp[k])%mod;
}
}
For(i,1,q)
{
si(s);
printf("%lld\n",dp[s]);
}
}
return 0;
}
四、树形DP
题目1: HDU1520---Anniversary party
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
typedef long long ll;
const int n_max=6e3+5;
const int inf=0x3f3f3f3f;
int dp[n_max][2],head[n_max],fun[n_max],p[n_max],cnt;
struct node
{
int to,next;
}e[n_max];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int cur)
{
dp[cur][1]=fun[cur];
for(int i=head[cur];i;i=e[i].next)
{
int v=e[i].to;
dfs(v);
dp[cur][0]+=max(dp[v][0],dp[v][1]);
dp[cur][1]+=dp[v][0];
}
}
int main()
{
int n,u,v;
scanf("%d",&n);
For(i,1,n)
scanf("%d",&fun[i]);
while(~scanf("%d%d",&u,&v)&&u&&v)
{
p[u]=v;
add(v,u);
}
int t=1;
while(p[t]) t=p[t];
dfs(t);
printf("%d\n",max(dp[t][0],dp[t][1]));
return 0;
}
以任意点为根dfs遍历整棵树,当去除某个节点时,会将树分成两棵子树,分别是向其父亲节点延伸和向其子节点延伸的树,对于节点i,定义dp[i]:去掉i产生的两棵树中节点数较大的数值,sum[i]:节点i的子树节点数(包括其本身);则有 dp[i]=max(n-sum[i],sum[k],k为他的子节点中子树节点数最大的一个);
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const int n_max=1e4+10;
int head[n_max],dp[n_max],sum[n_max],cnt,n;
struct edge
{
int nex,to;
}e[2*n_max];
void add(int u,int v)
{
e[++cnt].to=v;
e[cnt].nex=head[u];
head[u]=cnt;
}
int dfs(int cur,int last)
{
sum[cur]=1;
int temp=0;
for(int i=head[cur];i;i=e[i].nex)
{
int v=e[i].to;
if(v==last) continue;
int x=dfs(v,cur);
temp=max(temp,x);
sum[cur]+=x;
}
dp[cur]=max(n-sum[cur],temp);
return sum[cur];
}
int main()
{
int u,v;
n=read();
For(i,1,n-1)
{
u=read(),v=read();
add(u,v);
add(v,u);
}
dfs(1,0);
int flag=0;
For(i,1,n)
{
if(2*dp[i]<=n)
{
flag=1;
printf("%d\n",i);
}
}
if(!flag)
printf("NONE\n");
return 0;
}
注:由于树的特性是无环,所以可以直接dfs(cur, last),如果由当前节点cur延伸出去的边中有边的端点为父亲节点last,则跳过此边,即可避免重复访问。
题目3:HDU6446---Tree and Permutation
先了解一下低配版:HDU2376---Average distance
设树有n个顶点,对于任意一条树边,如果将其去除,树将会分成两棵,点的数量分别为m和n-m,左右两侧的点都必须经过这条边到达另一边的点,因而对于某一条边,枚举所有路径,该边对路径和的贡献值为w*m*(n-m),dfs每条边即可得到路径和,总共有n!条路径;而dfs每条边可以转化为dfs每个点,这样低配版就可以解决;
对于高配版,对于任意全排列n!和某两个点i和j,将i和j看成整体与其他点进行全排列,有2*(n-1)!种,也就是以任意点i和j 为端点的路径要走2*(n-1)!次,再乘上上面得出该路径贡献值为2*(n-1)!*w*m*(n-m)
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
const int n_max=1e5+10;
int head[n_max],cnt=0,n;
ll sum[n_max],fac[n_max],ans=0;
struct node
{
int to,nex;
ll w;
}e[n_max*2];
void add(int u,int v,ll w)
{
e[++cnt].to=v;
e[cnt].nex=head[u];
e[cnt].w=w;
head[u]=cnt;
}
void dfs(int u,int pre)
{
sum[u]=1;
for(int i=head[u];i;i=e[i].nex)
{
int v=e[i].to;
ll w=e[i].w;
if(v==pre) continue;
dfs(v,u);
sum[u]+=sum[v];
ans=(ans+sum[v]*(n-sum[v])*w)%mod;
}
}
int main()
{
int u,v;
ll w;
fac[0]=fac[1]=1;
for(ll i=2;i<=1e5;++i)
fac[i]=fac[i-1]*i%mod;
while(~scanf("%d",&n))
{
ans=0;
cnt=0;
memset(head,0,sizeof(head));
For(i,1,n-1)
{
scanf("%d%d%I64d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
dfs(1,-1);
printf("%I64d\n",2*ans*fac[n-1]%mod);
}
return 0;
}
五、概率DP
题目1:POJ2096--- Collecting Bugs
dp[i][j]: 目前已发现i种bug在j种子系统中,要达成目标的期望
dp[i][j]= (dp[i][j]+1)*p1+(dp[i+1][j+1]+1)*p2+(dp[i+1][j]+1)*p3+(dp[i][j+1]+1)*p4,
化简可得dp[i][j]=(1+dp[i+1][j+1]*p2+dp[i+1][j]*p3+dp[i][j+1]*p4)/(1-p1)
其中
可知dp[n][s]=0, dp[0][0]为所求结果
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cmath>
#include<map>
#define For(i,x,y) for(int i=x;i<=y;++i)
using namespace std;
typedef long long ll;
const int inf=0x3f3f3f3f;
const int n_max=1e3+5;
double dp[n_max][n_max];
int main()
{
double n,s;
scanf("%lf%lf",&n,&s);
for(int i=n;i>=0;--i)
{
for(int j=s;j>=0;--j)
{
if(i==n&&s==j) continue;
dp[i][j]=(n*s+dp[i+1][j+1]*(n-i)*(s-j)+dp[i+1][j]*(n-i)*j+dp[i][j+1]*i*(s-j))/(n*s-i*j);
}
}
printf("%.4lf",dp[0][0]);
return 0;
}
题目2:CodeForces 148D---Bag of mice
dp[i][j]:口袋里剩下i只白鼠和j只黑鼠时公主胜率
特殊情况:当i==0时,公主胜率为0;当j==0且i>=1时,公主胜率为1
一般情况:
如果公主抽到白鼠,对dp[i][j]贡献
如果公主抽到黑鼠,此时如果龙抽到白鼠公主获胜概率为0,对dp[i][j]无贡献;
接下来如果龙抽到黑鼠,然后会有两种情况,第一种是跑了白鼠,第二种是跑了黑鼠
因而最终为dp[i][j]贡献
注意判断j是否大于等于2或者大于等于3
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<cstring>
#include<string>
#include<cctype>
#include<cmath>
#include<map>
#include<vector>
#include<utility>
#define For(i,x,y) for(int i=x;i<=y;++i)
#define Fov(i,x,y) for(int i=x;i>=y;--i)
#define midf(a,b) ((a)+(b)>>1)
#define Num1(_) (_)<<1
#define Num2(_) ((_)<<1)|1
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
inline int read()
{
char ch=getchar(); int x=0, f=1;
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar();}
while('0'<=ch&&ch<='9') { x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
const int inf=0x3f3f3f3f;
const double pi=acos(-1.0);
const int n_max=1e3+10;
double dp[n_max][n_max];
int main()
{
int w,b;
scanf("%d%d",&w,&b);
For(i,0,w){
For(j,0,b){
if(i==0){
dp[i][j]=0;
continue;
}
if(j==0){
dp[i][j]=1;
continue;
}
dp[i][j]=1.0*i/(i+j);
if(j>=2)
dp[i][j]+=dp[i-1][j-2]* 1.0*j/(i+j)* 1.0*(j-1)/(i+j-1)* 1.0*i/(i+j-2);
if(j>=3)
dp[i][j]+=dp[i][j-3]* 1.0*j/(i+j)* 1.0*(j-1)/(i+j-1)* 1.0*(j-2)/(i+j-2);
}
}
printf("%.9lf\n",dp[w][b]);
return 0;
}