2013
12-09

# Parallel Expectations

We are to predict some facts about the behavior of a single processor designed for running two programs in parallel. Programs are sequences of commands according to the following grammar:

<Program> <Command> *
<Command> <Variable> := <Operand> <Operator> <Operand>
<Operator> + | –
<Operand> <Variable> | <Constant>

A <Variable> is a sequence of (at most 20) alphanumeric characters (A…Z, a…z, and 0…9) starting with a letter (not case sensitive). A <constant> is an unsigned integer number (less than 100). There may be arbitrary number of blank or tab characters between tokens.

Before execution, programs are translated into machine language. A statement of the form X := Y + Z is translated to the following set of machine instructions:

Mov R1, Y
Mov R2, Z
Add R1, R2
Mov X, R1

A MOV instruction copies the content of its second operand into its first operand. An Add (Sub) instruction, adds (subtracts) its second operand from its first operand and the result is stored in the first operand. Note that Y and Z denote either a variable or an integer constant. Instructions generated for the command X := Y – Z is similar to the above instructions, except that Sub command is used instead of Add.

The processor is given two machine language programs and starts executing them from the first instruction. In each step, it randomly selects one of the two programs and runs the next instruction from the selected program. This continues until one program reaches its end. In this situation, the remaining instructions from the other one are executed sequentially to the end and the processor stops. It is assumed that all variables are shared between two programs, but each program has a separate register set. The goal of this program is to compute the expected final value of all variables among all possible executions of the programs. More precisely, we want to consider every possible execution of the two programs and for each variable, calculate the average of its final value in different executions. It is assumed that the initial value of all variables is zero.

The first line of the input file contains a single integer t (1 ≤ t ≤ 10), the number of test cases, followed by the input data for each test case. The data for each test case consists of a pair of programs. Each program is written as a sequence of consecutive lines, each line containing exactly one command. Programs end with a line containing only the word END. You may assume that no variable in any program is named ‘END‘. There is no blank line between programs of one test case. There are at least one and at most 25 lines in each program. Total number of variables in two programs is no more than 10.

For each test case, the output file should contain the expected final value of all variables in alphabetical order of variable names (digits precede letters in this order). Output for different test cases should be separated by exactly one blank line. Round the numbers in the output to 4 digits after decimal point. Do not omit trailing zeros after decimal point (e.g. write 1.2000 instead of 1.2).

1
S := 1 + 3
END
S := S+S
END

3.0000

则可以推出，T[ i ][ j ] = (T[ i-1 ][ j ]*P1+T[ i ][ j-1 ]*P2)/(P1+P2) 不过需要注意几点：
1.理解题意很重要，这个题很容易误解 。
如果认为每种指令执行的情况是一样的话，就会求错方程：(设N[ i ][ j ]为状态T[ i ][ j ]数量 )
P1 = N[ i-1 ][ j ]/N[ i ][ j ]
P2 = N[ i ][ j-1 ]/N[ i ][ j ]
下面是POJ discuss 里得解释，看了这个就会明白了。
Example :
Exactly one execution of the sample input results in S=8, and the
probability of that execution is not 1/C(8,4)=1/70, but (1/2)^4=1/16,
since the program automatically execute remaining operations.
It is true that there are 70 different executions, but not all of
them have the same probability.
正确的概率计算为：(设N1为程序1指令总条数，N2为程序2指令总条数 )
if ( i == N1 && j == N2 ) P1 = P[ i-1 ][ j ]  ,P2 = P[ i ][ j-1 ]
if ( i  < N1 && j == N2 ) P1 = P[ i-1 ][ j ]  ,P2 = P[ i ][ j-1 ]/2
if ( i == N1 && j  < N2 ) P1 = P[ i-1 ][ j ]/2,P2 = P[ i ][ j-1 ]
if ( i  < N1 && j  < N2 ) P1 = P[ i-1 ][ j ]/2,P2 = P[ i ][ j-1 ]/2
P[ i ][ j ] = P1 + P2

#include<string.h>
#include<iostream>
#include<sstream>
#include<string>
#include<stdio.h>
#include<map>
using namespace std;
int l[2],n;
string v1[30][2],v2[30][2],v3[30][2];//存储变量
char op[30][2];//存储操作符
int n1[30][2],n2[30][2],n3[30][2];//存储常量
map<string,int>mp;//hash
struct node
{
double t[12];
double r[2][2];
double p;
void init()
{
memset(t,0,sizeof(t));
memset(r,0,sizeof(r));
p=1.0;
}
void run(int id,int o)
{
int V1,V2,V3;
double u1,u2;
V1=mp[v1[(o-1)/4][id]];
V2=mp[v2[(o-1)/4][id]];
V3=mp[v3[(o-1)/4][id]];
if(V1)
u1=t[V1];
else
u1=n1[(o-1)/4][id];
if(V2)
u2=t[V2];
else
u2=n2[(o-1)/4][id];
switch(o%4)
{
case 1:r[0][id]=u1;break;
case 2:r[1][id]=u2;break;
case 3:
if(op[(o-1)/4][id]=='+')
r[0][id]=r[0][id]+r[1][id];
else
r[0][id]=r[0][id]-r[1][id];break;
case 0:t[V3]=r[0][id];break;
}

}
}dp[125][125];
bool check(string s,int &a)//读取常量
{
if(s[0]-'0'>=0&&s[0]-'0'<=9)
{
istringstream sin(s);
sin>>a;
return 0;
}
return 1;
}
void solve()
{
int i,j,k,k1,k2;
double p1,p2,p;
node f1,f2;
dp[0][0].init();
for(i=0;i<=l[0];i++)
for(j=0;j<=l[1];j++)
{
if(i|j)
{
if(i==0)
{
f1=dp[i][j-1];
p1=f1.p*0.5;p2=0;
f1.run(1,j);
}
else if(j==0)
{
f2=dp[i-1][j];
p2=f2.p*0.5;
p1=0;
f2.run(0,i);
}
else
{
f1=dp[i][j-1];
f2=dp[i-1][j];
p1=f1.p*(i==l[0]?1:0.5);
p2=f2.p*(j==l[1]?1:0.5);
f1.run(1,j);
f2.run(0,i);
}
dp[i][j].p=p=p1+p2;
for(k=1;k<=n;k++)
{
dp[i][j].t[k]=(f1.t[k]*p1+f2.t[k]*p2)/p;
}

for(k1=0;k1<=1;k1++)
for(k2=0;k2<=1;k2++)
{
dp[i][j].r[k1][k2]=(f1.r[k1][k2]*p1+f2.r[k1][k2]*p2)/p;
}
}
}
for(i=1;i<=n;i++)
printf("%.4lf\n",dp[l[0]][l[1]].t[i]);
printf("\n");
}
int main()
{
int ti,i,j;
char ch;
string str;
scanf("%d",&ti);
while (cin.peek()=='\n')
getchar();
while(ti--)
{
mp.clear();
for(i=0;i<=1;i++)
{
while(cin.peek()=='\n')//吸收多余空行
getchar();
for(j=0;1;j++)
{
v1[j][i]=v2[j][i]=v3[j][i]="";
n1[j][i]=n2[j][i]=n3[j][i]=0;
while(cin.peek()!=':'&&cin.peek()!='\n')
{
ch=getchar();
if(ch!=' ')
v3[j][i]+=toupper(ch);
}
if(v3[j][i]=="END")
break;
if(check(v3[j][i],n3[j][i])==1)
mp[v3[j][i]]++;
for(ch=getchar();cin.peek()==' ';)
ch=getchar();
for(ch=getchar();cin.peek()==' ';)
ch=getchar();
while(cin.peek()!='+'&&cin.peek()!='-')
{
ch=getchar();
if(ch!=' ')
v1[j][i]+=toupper(ch);
}
if(check(v1[j][i],n1[j][i])==1)
mp[v1[j][i]]++;
scanf("%c",&op[j][i]);
for(;cin.peek()==' ';)
ch=getchar();
while(cin.peek()!=' '&&cin.peek()!='\n')
{
ch=getchar();
if(ch!=' ')
v2[j][i]+=toupper(ch);
}
if(check(v2[j][i],n2[j][i])==1)
mp[v2[j][i]]++;
while(cin.peek()==' ')
ch=getchar();
while(cin.peek()=='\n')
getchar();
}
l[i]=j*4;
}
map<string,int>::iterator it=mp.begin();//map内部已排序
for(i=1;it!=mp.end();i++,it++)
mp[it->first]=i;
n=i-1;
solve();
}
}

1. 换句话说，A[k/2-1]不可能大于两数组合并之后的第k小值，所以我们可以将其抛弃。
应该是，不可能小于合并后的第K小值吧

2. 嗯 分析得很到位，确实用模板编程能让面试官对你的印象更好。在设置辅助栈的时候可以这样：push时，比较要push的elem和辅助栈的栈顶，elem<=min.top()，则min.push(elem).否则只要push（elem）就好。在pop的时候，比较stack.top()与min.top(),if(stack.top()<=min.top()),则{stack.pop();min.pop();}，否则{stack.pop();}.