T1:
玩具谜题
题目描述
小南有一套可爱的玩具小人,它们各有不同的职业。
有一天,这些玩具小人把小南的眼镜藏了起来。小南发现玩具小人们围成了一个圈,它们有的面朝圈内,有的面朝圈外。
这时 singer
告诉小南一个谜题:“眼镜藏在我左数第3个玩具小人的右数第1个玩具小人的左数第2个玩具小人那里。”
小南发现,这个谜题中玩具小人的朝向非常关键,因为朝内和朝外的玩具小人的左右方向是相反的: 面朝圈内的玩具小人,它的左边是顺时针方向,右边是逆时针方向;而面向圈外的玩具小人,它的左边是逆时针方向,右边是顺时针方向。
小南一边艰难地辨认着玩具小人,一边数着:
“singer
朝内,左数第3个是 archer
。
“archer
朝外,右数第1个是 thinker
。
“thinker
朝外,左数第2个是 writer
。
“所以眼镜藏在 writer
这里!”
虽然成功找回了眼镜,但小南并没有放心。如果下次有更多的玩具小人藏他的眼镜,或是谜题的长度更长,他可能就无法找到眼镜了。 所以小南希望你写程序帮他解决类似的谜题。这样的谜题具体可以描述为:
有n个玩具小人围成一圈,已知它们的职业和朝向。现在第 1个玩具小人告诉小南一个包含 m条指令的谜题,其中第i条指令形如“左数/右数第si个玩具小人”。
你需要输出依次数完这些指令后,到达的玩具小人的职业。
输入格式
从标准输入读入数据。
输入的第一行包含两个正整数 m,n,表示玩具小人的个数和指令的条数。
接下来n行,每行包含一个整数和一个字符串,以逆时针为顺序给出每个玩具小人的朝向和职业。其中0表示朝向圈内,1表示朝向圈外。 保证不会出现其他的数。字符串长度不超过10且仅由小写字母构成,字符串不为空,并且字符串两两不同。整数和字符串之间用一个空格隔开。
接下来 m行,其中第 i行包含两个整数ai,si,表示第 ii 条指令。若ai=0,表示向左数si个人;若ai=1,表示向右数si个人。 保证ai不会出现其他的数,1≤si<n。
输出格式
输出到标准输出。
输出一个字符串,表示从第一个读入的小人开始,依次数完m条指令后到达的小人的职业。
样例一
input
7 3
0 singer
0 reader
0 mengbier
1 thinker
1 archer
0 writer
1 mogician
0 3
1 1
0 2
output
writer
explanation
这组数据就是题目描述中提到的例子。
样例二
input
10 10
1 c
0 r
0 p
1 d
1 e
1 m
1 t
1 y
1 u
0 v
1 7
1 1
1 4
0 5
0 3
0 1
1 6
1 2
0 8
0 4
output
y
正解:模拟
解题报告:
将题目中的小人朝向和往左右走,看成一个0、1状态,容易发现我们可以通过异或和取模来实现每一步是走向编号变大还是编号变小。
注意事项:
涉及到取模时,一定要注意对于n取模之后的值域是[0,n-1],所以最好是一开始就把下标改成从0开始,避免不必要的错误。
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <string>
#include <cstdlib>
#include <algorithm>
#include <ctime>
#include <set>
#include <vector>
#include <queue>
using namespace std;
const int MAXN = 100011;
int n,m,dir[MAXN];
char ch[MAXN][14];
void work(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) {
scanf("%d",&dir[i]);
scanf("%s",ch[i]);
}
int x,y,ljh; x=1;
while(m--) {
scanf("%d%d",&ljh,&y);
if(dir[x]==0) {
if(ljh==0) {
x+=n; x-=y;
if(x>n) x%=n;
}
else{
x+=y;
if(x>n) x%=n;
}
}
else{
if(ljh==0) {
x+=y;
if(x>n) x%=n;
}
else{
x+=n; x-=y;
if(x>n) x%=n;
}
}
}
printf("%s",ch[x]);
}
int main()
{
work();
return 0;
}
// Made by zengzihang
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <ctime>
#include <queue>
#include <sstream>
#include <vector>
using namespace std;
const int Max=1000010;
long long n,m,head;
struct toy1{char job[105];int fx;};
toy1 a[Max];
int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
if(c=='-') {f=-1; c=getchar();}
for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=get_int();
m=get_int();
for(int i=1;i<=n;i++)
{
scanf("%d %s\n",&a[i].fx,a[i].job);
}
head=1;
for(int i=1;i<=m;i++)
{
int x,y;
scanf("%d%d",&x,&y);
if(a[head].fx==0)
{
if(x==0) head-=y;
if(x==1) head+=y;
}
else
{
if(x==0) head+=y;
if(x==1) head-=y;
}
while(head<1) head+=n;
while(head>n) head-=n;;
}
cout<<a[head].job<<endl;
return 0;
}
T2:
Description
Input
Output
输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。
Sample Input
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
Sample Output
HINT
25分
考虑此时 n 很小,可以对于每条路径上暴力模拟,经过某个点时可以看一下当前时刻,是否跟经过的点的 w 相等,如果相等,则贡献加一。
45分
注意到测试点 9−12 时,保证 m 条路径的出发点都是 1 ,那么我们可以考虑如果将 1 作为树根,那么一条路径怎样才能对于它经过的点产生贡献。
不难看出对于一个点 i i,只有在 deep[i]=w[i] ,才有可能有贡献。
我在考场上是直接用的链剖 + 线段树,因为这就变成模板题了,而且 n 不到 10w ,尽管复杂度偏高,但是不易错。
直接对于每条路径经过的点在线段树上增加 1 次经过次数,显然只有 deep 与 w 相等的点才会产生贡献。
事实上对于 S=1 的情况有线性的算法,正解会详细介绍,不再赘述。
60分
注意到测试点 6−8 时,题目保证树退化成链。我们观察一下对于链而言,有什么特别的地方。首先要明确,此时m条路径在链上肯定是要么往左要么往右,即 S<=T 或者 S>T 。
先只考虑 S<=T 的情况,如果对于 S 到 T 之间的点i,要产生贡献的话,肯定满足 i−S=w[i] ,移项可得 S=i−w[i] 时才可以满足要求。
注意到等式右边只与 i i本身有关,不妨设为 K[i] ,所以题目变成了查询 S 到 T 之间 K[i] 等于 S 的 i 的数量。
因为题目只涉及到首和尾,我们可以很容易联想到差分,即对于 S 打上 +1 标记, T 打上 −1 标记。
根据上述思路,我们考虑具体做法:对于每个点 i i,我们很容易发现只有从一个特定的点出发才有可能对 i i产生贡献。
我们考虑维护一个统计数组 A , A[k] 表示的是处理到当前的结点时,从 k 出发的路径(而且还没有走到终点)有多少条。
这样对于每个点 i i,我们只要查询一下所对应的 A[K[i]] 就可以了,根据上面的分析,这就是我们的答案了。
有一点注意处理:处理一个点 i i时,我们需要把以 i i为起点的路径加入统计数组 A ,再计算这个结点的贡献,最后再把以这个结点为终点的路径从 A 中消除,具体可以用 vector 实现(上述处理顺序的必要性仔细想想就很容易想通了)。
而对于 S>T 的情况完全类似,只是需要把 K[i] 定义为 i+w[i] ,其余做法完全类似。
100分
题目中设计的几个档次的部分分其实暗示已经很明显了。
链的做法离正解就不远了。
而 S=1 和 T=1 是在告诉我们什么呢?
拆路径!
很容易发现,一条 S 到 T 的路径可以拆成一条 S 到 LCA 的路径和 LCA 到 T 的路径,然后对于这两条路径,一条往上,一条往下,都可以对应成链的处理方式了!
考虑对于每条路径,先将其拆分成两条路径(为了简化对 LCA 在两条路径中都出现的各种情况,我们可以先就让 LCA 出现两次,如果最后发现 LCA 是有贡献的,只需 −1 即可),同样,我们先只考虑向上的路径。
如果我们对于 S 在统计数组 A 上打上 1 的标记, LCA 在统计数组 A 上打上 −1 的标记,那么题目转化为求一个点的子树和。
考虑上述做法正确性:因为只有 S 到 LCA 路径之间的点会产生贡献,而当这个点位于路径之间时,子树和会产生 1 的贡献,而在 S 的子树中或者 LCA 的上方都不会产生贡献。
具体实现呢?
对于一个点 i ,产生贡献的条件是 deep[S]−deep[i]=w[i] ,同样令 K[i]=deep[i]+w[i] ,当我们 dfs 到 i 时查询 A[k[i]] 的值即为贡献。
为了保证正确性,我们思考统计答案的方式和顺序。
首先我们肯定是在处理完 i i的子树之后再来处理 i (想想就知道了),然后我们需要再把以 i 出发的向上的路径加入统计数组,再进行查询,最后把以 i i为终点的路径所产生的贡献在统计数组 A 中消除即可。
注意到我们上面维护的仅仅是一个点的深度,由于同一深度的点很多,所以我们查询的时候会发现会把不在同一子树的点统计入答案,那怎么办呢?我们考虑对于一个点要查询子树和,肯定是只要单独地考虑这一个子树的贡献,所以我们可以记录进入 i i时 A[k[i]] 的值,再在访问完 i i的子树之后统计答案时,看一下此时新的 A[k[i]] 的值。
容易发现新的值减掉进入时的,才是真正的 i 的子树中的 A[k[i]] 的值。
这样我们就可以避免把别的子树的答案统计进来了。
对于向下的点做法类似,有一点复杂的地方就是等式变成了 deep[T]−deep[i]=len−w[i] ( len 为路径长度),发现如果这样做的话会出现负数,那么我们就把统计数组向右平移 3∗105 位就可以了。
上述做法如果采用的是倍增求 LCA 的话,复杂度就是 O(nlogn) ;
如果用 tarjan 离线求 LCA 的话,可以做到 O(n+m) 。
注意事项
对于树上每个结点,统计答案时不能直接查询在统计数组中的对应的路径条数,
而应该统计 dfs 进入 i 时,和访问完 i i的子树时的变化量。
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = 300011;
const int MAXM = 600011;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void init(int x,int fa){ for(int i=first[x];i;i=next[i]) { int v=to[i]; if(v==fa) continue; deep[v]=deep[x]+1; init(v,x); f[v][0]=x; } }
inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
}
inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
}
inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
}
inline void work(){
n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=1;i<=n;i++) w[i]=getint(); deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=m;i++) {
a[i].s=getint(),a[i].t=getint(),val[a[i].s]++;
a[i].lca=lca(a[i].s,a[i].t),a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(1,0);
for(int i=1;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(1,0);
for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
int main()
{
work();
return 0;
}
O(n+m):
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = 300011;
const int MAXM = 600011;
int n,m,ecnt,first[MAXN],next[MAXM],to[MAXM],f[MAXN][20],deep[MAXN],ans[MAXN],val[MAXN],tong[MAXN],MAXD,w[MAXN],num[1000011];
int head[MAXN],tt[MAXM],nn[MAXM],father[MAXN],vis[MAXN];
vector<int>ljh[MAXN],ljh2[MAXN],ljh3[MAXN];
struct node{ int s,t,lca,len;}a[MAXN];
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline void LINK(int x,int y){ nn[++ecnt]=head[x]; head[x]=ecnt; tt[ecnt]=y; }
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline void init(int x,int fa){
father[x]=x; vis[x]=1;
for(int i=head[x];i;i=nn[i]) {
int v=tt[i];
if(x==a[v].s&&vis[a[v].t]) a[v].lca=find(a[v].t);
if(x==a[v].t&&vis[a[v].s]) a[v].lca=find(a[v].s);
}
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
deep[v]=deep[x]+1; init(v,x); father[v]=x;
f[v][0]=x;
}
}
inline int lca(int x,int y){
if(deep[x]<deep[y]) swap(x,y); int t=0; while((1<<t)<=deep[x]) t++; t--;
for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=f[x][i]; if(x==y) return y;
for(int i=t;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0];
}
inline void dfs(int x,int fa){
int now=w[x]+deep[x],cun; if(now<=MAXD) cun=tong[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
dfs(v,x);
}
tong[deep[x]]+=val[x]; if(now<=MAXD) ans[x]=tong[now]-cun;
for(int i=0,ss=ljh[x].size();i<ss;i++) tong[deep[ljh[x][i]]]--;
}
inline void DFS(int x,int fa){
int now=deep[x]-w[x],cun; now+=300000; cun=num[now];
for(int i=first[x];i;i=next[i]) {
int v=to[i]; if(v==fa) continue;
DFS(v,x);
}
for(int i=0,ss=ljh2[x].size();i<ss;i++) num[300000+ljh2[x][i]]++;
ans[x]+=num[now]-cun;
for(int i=0,ss=ljh3[x].size();i<ss;i++) num[300000+ljh3[x][i]]--;
}
inline void work(){
n=getint(); m=getint(); int x,y; for(int i=1;i<n;i++) { x=getint(); y=getint(); link(x,y); link(y,x); }
for(int i=1;i<=n;i++) w[i]=getint(); ecnt=0;
for(int i=1;i<=m;i++) { a[i].s=getint(),a[i].t=getint(),val[a[i].s]++; LINK(a[i].s,i); LINK(a[i].t,i);}
deep[1]=1; init(1,0); for(int i=1;i<=n;i++) MAXD=max(MAXD,deep[i]);
for(int j=1;j<=19;j++) for(int i=1;i<=n;i++) f[i][j]=f[f[i][j-1]][j-1];
for(int i=1;i<=m;i++) {
a[i].len=deep[a[i].s]+deep[a[i].t]-deep[a[i].lca]*2;
ljh[a[i].lca].push_back(a[i].s);
}
dfs(1,0);
for(int i=1;i<=m;i++) {
ljh2[a[i].t].push_back(deep[a[i].t]-a[i].len);
ljh3[a[i].lca].push_back(deep[a[i].t]-a[i].len);
}
DFS(1,0);
for(int i=1;i<=m;i++) if(deep[a[i].s]-deep[a[i].lca]==w[a[i].lca]) ans[a[i].lca]--;
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
}
int main()
{
work();
return 0;
}
Description
Input
Output
Sample Input
2 1 2
1 2 1
0.8 0.2 0.5
1 2 5
1 3 3
2 3 1
Sample Output
24分
注意到有6个测试点m=0,则说明不能提出申请,那么只需要求出全图的两两之间的最短路,路径唯一确定。
52分
注意到另外有7个测试点m=1,只能提出一次申请。我们可以直接枚举在哪里提出申请,再与不申请的时候取一个min就可以了。
76分
注意到还有6个测试点m=2,只能提出两次申请,暴力枚举哪两个点申请即可。
80分
其实我们并不需要分那么多类情况讨论,考虑直接爆搜,在每个点是否提出申请,最后暴力计算贡献。因为这样的复杂度是C(n,m)的,所以m<=2和n<=10是完全没有问题的,直接可以用搜索拿到80分。
100分
这显然是一道概率DP裸题。考虑f[i][j][0、1]表示前i堂课,已经申请了j次,这次申不申请的最小期望值,则:
(dis(i,j)表示i到j的最短距离)
需要说明的是,期望是可以线性相加的,也就是说一条路径上的距离总期望值实际上就等于每两个相邻点之间的距离的期望值的总和。另外上面的转移式可以这样理解:在我们已经提出申请的情况下,对于所有情况的讨论就是在现有的申请情况下的能得到的距离期望值,事实上是根据计算了每条边的贡献。
这样即可获得满分。时间复杂度:O(V3+nm)
注意事项
如果状态的0、1表示的是地点在c还是d的话会有不少问题,因为压根就不能体现出申请的时候的成功与失败结果,但是对于m<=2的点基本不会出错,大点的出错概率也不是特别大
代码如下:
//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
using namespace std;
typedef long long LL;
const int MAXN = 2011;
const int MAXD = 311;
const int inf = (1<<29);
int n,m,D,bian,c[MAXN],d[MAXN],dis[MAXD][MAXD];
double f[MAXN][MAXN][2],k[MAXN],ans;
inline int getint(){
int w=0,q=0; char c=getchar(); while((c<'0'||c>'9') && c!='-') c=getchar();
if(c=='-') q=1,c=getchar(); while (c>='0'&&c<='9') w=w*10+c-'0',c=getchar(); return q?-w:w;
}
inline void work(){
n=getint(); m=getint(); D=getint(); bian=getint(); int x,y,z,lim; double minl;
for(int i=1;i<=n;i++) c[i]=getint(); for(int i=1;i<=n;i++) d[i]=getint(); for(int i=1;i<=n;i++) scanf("%lf",&k[i]);
for(int i=1;i<=D;i++) for(int j=1;j<=D;j++) dis[i][j]=inf;
for(int i=1;i<=bian;i++) {
x=getint(); y=getint(); z=getint(); if(dis[x][y]==inf) dis[x][y]=dis[y][x]=z;
else dis[x][y]=min(dis[x][y],z),dis[y][x]=dis[x][y];
}
for(int l=1;l<=D;l++) for(int i=1;i<=D;i++) if(i!=l) for(int j=1;j<=D;j++) if(j!=l&&i!=j) dis[i][j]=min(dis[i][l]+dis[l][j],dis[i][j]);
for(int i=1;i<=n;i++) for(int j=0;j<=m;j++) f[i][j][0]=f[i][j][1]=1e30; f[1][0][0]=f[1][1][1]=0;
for(int i=1;i<=D;i++) dis[i][i]=0;
for(int i=2;i<=n;i++) {
lim=min(i,m);
for(int j=0;j<=lim;j++) {
minl=f[i-1][j][1]+dis[c[i-1]][c[i]]*(1.0-k[i-1])+dis[d[i-1]][c[i]]*k[i-1];
minl=min(minl,f[i-1][j][0]+dis[c[i-1]][c[i]]);
f[i][j][0]=min(f[i][j][0],minl);
if(j>=1) {
minl=f[i-1][j-1][1]+dis[c[i-1]][c[i]]*(1.0-k[i])*(1.0-k[i-1])+dis[c[i-1]][d[i]]*(1.0-k[i-1])*k[i];
minl+=dis[d[i-1]][c[i]]*k[i-1]*(1-k[i])+dis[d[i-1]][d[i]]*k[i-1]*k[i];
minl=min(minl,f[i-1][j-1][0]+dis[c[i-1]][d[i]]*k[i]+dis[c[i-1]][c[i]]*(1.0-k[i]));
f[i][j][1]=min(f[i][j][1],minl);
}
}
}
ans=1e30; for(int i=0;i<=m;i++) ans=min(ans,min(f[n][i][0],f[n][i][1]));
printf("%.2lf",ans);
}
int main()
{
work();
return 0;
}
// Made by zengzihang
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <cmath>
#include <cctype>
#include <ctime>
#include <vector>
#include <queue>
#include <sstream>
using namespace std;
const int Max=2010;
int n,m,v,e;
int dis[Max][Max];
int c[Max],d[Max];
double ans,p[Max],f[Max][Max][2];
int get_int()
{
int x=0,f=1;
char c;
for(c=getchar();(c<'0'||c>'9')&&(c!='-');c=getchar());
if(c=='-') {f=-1;c=getchar();}
for(;c>='0'&&c<='9';c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
int main()
{
//freopen("lx.in","r",stdin);
//freopen("lx.out","w",stdout);
n=get_int();
m=get_int();
v=get_int();
e=get_int();
for(int i=1;i<=n;i++) c[i]=get_int();
for(int i=1;i<=n;i++) d[i]=get_int();
for(int i=1;i<=n;i++) scanf("%lf",&p[i]);
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
if(i!=j) dis[i][j]=1e+8;
for(int i=1;i<=e;i++)
{
int x,y,z;
x=get_int();
y=get_int();
z=get_int();
dis[x][y]=min(dis[x][y],z);
dis[y][x]=min(dis[y][x],z);
}
for(int k=1;k<=v;k++)
for(int i=1;i<=v;i++)
for(int j=1;j<=v;j++)
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
f[i][j][0]=f[i][j][1]=1e+8;
f[1][1][1]=f[1][0][0]=0;
for(int i=1;i<n;i++)
for(int j=0;j<=m;j++)
{
f[i+1][j][0]=min(f[i][j][1]+p[i]*dis[d[i]][c[i+1]]+(1.0-p[i])*dis[c[i]][c[i+1]],f[i][j][0]+dis[c[i]][c[i+1]]);
f[i+1][j+1][1]=min(f[i][j][1]+p[i]*p[i+1]*dis[d[i]][d[i+1]]+(1.0-p[i])*p[i+1]*dis[c[i]][d[i+1]]+p[i]*(1.0-p[i+1])*dis[d[i]][c[i+1]]+(1.0-p[i])*(1.0-p[i+1])*dis[c[i]][c[i+1]],f[i][j][0]+p[i+1]*dis[c[i]][d[i+1]]+(1.0-p[i+1])*dis[c[i]][c[i+1]]);
}
ans=1e+8;
for(int i=0;i<=m;i++)
ans=min(ans,min(f[n][i][0],f[n][i][1]));
printf("%0.2f",ans);
return 0;
}
动态规划是近年来NOIP的热点,这道概率DP算是新题型,未来几年可能考到,有关概率DP的经典题和技巧详见
http://blog.csdn.net/auto_ac/article/details/9907881
总结得很好,可以参考一下
以上题解及部分代码分别转载自ljh2000的博客
http://www.cnblogs.com/ljh2000-jump/p/6189050.html
http://www.cnblogs.com/ljh2000-jump/p/6189054.html
http://www.cnblogs.com/ljh2000-jump/p/6189053.html