描述
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 is a sequence of (at most 20) alphanumeric characters (A...Z, a...z, and 0...9) starting with a letter (not case sensitive). A 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.
我们要预测一个处理器的行为,设计并行运行两个程序的一些事实。程序是根据以下语法的命令序列:
< 程序 >-> < 命令 > * < 命令 >-> < 变量 > : = < 操作员 > < 操作员 > < 操作员 >-> + |-< 操作员 >-> < 变量 > | < 常量 >
A 是一个由(最多20个)字母数字字符组成的序列(A。.Z A..Z 和0... 9)以字母开头(不区分大小写)。A 是一个无符号整数(小于100)。标记之间可能有任意数量的空白字符或制表符。在执行之前,程序被翻译成机器语言。形式 X: = Y + Z 的语句被翻译成以下机器指令集: MOV R1,YMov R2,ZAdd R1,R2Mov X,R1A MOV 指令将其第二个操作数的内容复制到其第一个操作数中。Add (Sub)指令将其第二个操作数与第一个操作数相加(减去) ,结果存储在第一个操作数中。注意 Y 和 Z 表示变量或整数常量。为命令 X: = Y-Z 生成的指令类似于上面的指令,只是使用 Sub 命令代替 Add。处理器得到两个机器语言程序,并从第一条指令开始执行它们。在每个步骤中,它随机选择两个程序中的一个,并从选定的程序中运行下一条指令。这种情况一直持续到一个程序结束为止。在这种情况下,来自另一个指令的其余指令将按顺序执行到结束,处理器将停止。假设所有变量在两个程序之间共享,但是每个程序有一个单独的寄存器集。这个程序的目标是计算所有可能执行的程序中所有变量的期望最终值。更准确地说,我们希望考虑两个程序的每一种可能的执行,并且对于每一个变量,计算不同执行中其最终值的平均值。假设所有变量的初始值为零。
输入
The first line of the input 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.
输入的第一行包含一个整数 t (1 < = t < = 10) ,即测试用例的数量,然后是每个测试用例的输入数据。每个测试用例的数据由一对程序组成。每个程序都被写成一系列连续的行,每一行恰好包含一个命令。程序以只包含单词 END 的行结束。您可以假定任何程序中的任何变量都不命名为“ END”。一个测试用例的程序之间没有空行。每个程序中至少有一行,最多25行。两个程序中的变量总数不超过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).
对于每个测试用例,输出文件应该包含所有变量的预期最终值,字母顺序为变量名(数字在这个顺序的字母之前)。不同测试用例的输出应该正好由一个空行分隔。将输出中的数字四舍五入到小数点后的4位。不要忽略小数点后面的零(例如写1.2000而不是1.2)。
样例输入
1 S := 1 + 3 END S := S+S END 样例输出 3.0000 来源 Tehran 2001
分析
这道题比较麻烦。
设状态 T[ i ][ j ] 为程序1运行i条指令,程序2运行j条指令后的变量平均值。P1为程序1指令i的概率,P2为程序2指令j的概率。则可以推出,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 里的解释,看了这个就会明确了:
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. (只要执行一次样例输入,结果就是 S = 8。执行的概率不是1/C (8,4) = 1/70,而是(1/2) ^ 4 = 1/16,因为程序会自动执行剩余的操作。确实有70种不同的执行,但不是所有的执行都有相同的概率。)
正确的概率计算为:(设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();
}
}
给个赞和关注吧