Hdu 5396 Expression
链接
Expression
题意
给定n个数和n-1个操作符(+,-,*),求所有不同的运算顺序的结果的和
注:这里乘法和加减没有优先级之分。如1+2*3可能是1+(2*3)或(1+2)*3
分析
区间DP
若一个表达式有num个操作符,则有num!种不同的计算方式
用dp[l][r]表示第l个数到第r个数组成的所有可能性的表达式和,枚举最后操作k,
若为加号,对于右边不同的组合(r-k-1)!种,左边的数每次都要被加一次,
同理左边不同的组合(k-l)!种,右边的数每次也要被加一次。
即$ temp=(r-k-1)!*dp[l][k]+(k-l)!*dp[k+1][r] $,
减法同理$ temp=(r-k-1)!*dp[l][k]-(k-l)!*dp[k+1][r] $
若为乘法$ temp=dp[l][k]*dp[k+1][r] $,乘法满足分配律,算出分别左右情况的总和相乘即可
还有一点,左边的顺序和右边的顺序的确定,假设左边有f1个符号,右边有f2个符号,
就有$cnt=\binom{f1+f2}{f1}$种组合,即左边和右边的操作顺序不同,运算顺序也不同
即$ dp[l][r]+=temp*cnt $
参考代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; typedef long long LL; const LL MOD=1000000007LL; const int N=105; LL dp[N][N],c[N][N],f[N]; char ope[N]; void init() { c[0][0]=f[0]=1; for(int i=1;i<N;i++){ c[i][0]=c[i][i]=1; f[i]=f[i-1]*i%MOD; for(int j=1;j<N;j++) c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD; } } void solve(int n) { for(int len=2;len<=n;len++){ for(int l=0;l+len<=n;l++){ int r=l+len-1; LL temp; for(int k=l;k<r;k++){ if(ope[k]=='+') temp=(f[r-k-1]*dp[l][k]%MOD+f[k-l]*dp[k+1][r]%MOD)%MOD; else if(ope[k]=='-') temp=((f[r-k-1]*dp[l][k]%MOD-f[k-l]*dp[k+1][r]%MOD)%MOD+MOD)%MOD; else if(ope[k]=='*') temp=dp[l][k]%MOD*dp[k+1][r]%MOD; temp=temp*c[len-2][k-l]%MOD; dp[l][r]=(dp[l][r]+temp)%MOD; } } } } int main() { int n; init(); while(scanf("%d",&n)!=EOF){ memset(dp,0,sizeof(dp)); for(int i=0;i<n;i++) scanf("%I64d",&dp[i][i]); scanf("%s",ope); solve(n); printf("%I64d\n",dp[0][n-1]); } return 0; }
|
Hdu 5399 Too Simple
链接
Too Simple
题意
有m个函数,$f_1,f_2,\cdots,f_m:{1,2,\cdots,n}\to{1,2,\cdots,n}$
即$x \in {1,2,\cdots,n},f(x)\in {1,2,\cdots,n}$
给定n和m,即m个函数对应的函数值,若为-1,则该函数未知,求有多少种可能性满足
$$f_1(f_2(\cdots f_m(i)))=i (1\leq i\leq n)$$
分析
首先要求每个$f_i$是个排列,即当i≠j时,f[i]≠f[j] ,否则如果某个$f_i$
将两个数映射向同一个数,那么最后这两个数得到的值一定相同。
如果还剩一个位置为-1,那么这个排列是唯一确定的,只有一种
若有cnt个-1,则有$(n!)^{cnt-1}$种可能性
若所有的函数都确定,除了满足必须都是一个排列外,还得满足题设的条件
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| #include<stdio.h> #include<string.h> typedef long long LL; const int N=105; const LL MOD=1000000007LL; LL f[N]; int n,m,y[N][N]; void init() { f[0]=f[1]=1; for(int i=2;i<N;i++) f[i]=f[i-1]*i%MOD; } LL powMod(LL a,LL b) { LL ans=1; while(b){ if(b&1) ans=ans*a%MOD; a=a*a%MOD; b>>=1; } return ans; } bool judge() { for(int i=1;i<=n;i++){ int x=i; for(int j=m;j>=1;j--) x=y[j][x]; if(x!=i) return false; } return true; } int main() { init(); while(scanf("%d%d",&n,&m)!=EOF){ int cnt=0; bool flag=true; for(int i=1;i<=m;i++){ scanf("%d",&y[i][1]); if(y[i][1]==-1) cnt++; else{ bool vis[N]={false}; vis[y[i][1]]=true; for(int j=2;j<=n;j++){ scanf("%d",&y[i][j]); if(!vis[y[i][j]]) vis[y[i][j]]=true; else flag=false; } } } if(!flag) printf("0\n"); else if(cnt==0){ if(judge()) printf("1\n"); else printf("0\n"); } else printf("%I64d\n",powMod(f[n],cnt-1)); } return 0; }
|
Hdu 5400 Arithmetic Sequence
链接
Arithmetic Sequence
题意
给定n个数{a1,a2..an},及d1,d2,求有多少个区间[l,r]满足[l,i]为公差为d1的等差数列,
[i,r]为公差为d2的等差数列 $(i \in [l,r]) $
分析
对于单个数肯定满足条件,如某区间只形成公差为d1或d2的等差数列也满足题意
对于长度大于等于2的区间,可以枚举起点i,找到最长的满足d1,d2的序列j
那么就有 $\binom{j-i+1}{2}=(j-i+1)*(j-i)/2$个满足条件,然后i再从j开始枚举
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include<stdio.h> #include<string.h> #include<algorithm> using namespace std; const int N=100010; typedef long long LL; LL a[N],b[N]; int main() { int n,d1,d2; while(scanf("%d%d%d",&n,&d1,&d2)!=EOF){ for(int i=1;i<=n;i++) scanf("%I64d",&a[i]); for(int i=1;i<n;i++) b[i]=a[i+1]-a[i]; int i=1; LL ans=n; while(i<n){ int j=i; while(j<n&&b[j]==d1) j++; while(j<n&&b[j]==d2) j++; LL len=j-i+1; ans+=len*(len-1)/2; if(len!=1) i=j; else i++; } printf("%I64d\n",ans); } return 0; }
|
Hdu 5402 Travelling Salesman Problem
链接
Travelling Salesman Problem
题意
给定n*m的迷宫,每个格的数都是非负的,Teacher Mai想要从迷宫的左上角(1,1)到
迷宫的右下角(n,m),每次只能走到相邻的格,其不能走出迷宫,每个格最多只能走一次
求走过的路径的整数之和最大为多少以及他走的路径。
分析
因为每个格子都是非负整数,而且规定每个格子只能走一次,所以为了使和尽可能大,必定是走的格子数越多越好。这样我们就需要考虑一下是不是所有的格子都可以走。
若n、m中至少有一个是奇数的话,必然能够遍历每一个格子,
m为奇数
n为奇数时
当n、m都为偶数,根据棋盘黑白染色可得,当假设(1,1)与(n,m)都为黑色,那么这条路径势必黑色格子数会比白色格子数多1,而棋盘中黑白格子数是相等的,所以棋盘中有一个白格子不会被经过。
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
| #include<stdio.h> const int INF=0x3f3f3f3f; const int N=105; int main() { int n,m; while(scanf("%d%d",&n,&m)!=EOF){ int sum=0,x,minx=INF,posi,posj; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++){ scanf("%d",&x); sum+=x; if(((i+j)&1)&&x<minx){ minx=x; posi=i; posj=j; } } if(m&1||n&1){ printf("%d\n",sum); if(n&1){ for(int i=1;i<=n;i++){ for(int j=1;j<m;j++){ if(i&1) printf("R"); else printf("L"); } if(i<n) printf("D"); else printf("\n"); } } else{ for(int j=1;j<=m;j++){ for(int i=1;i<n;i++){ if(j&1) printf("D"); else printf("U"); } if(j<m) printf("R"); else printf("\n"); } } } else{ printf("%d\n",sum-minx); for(int i=1;i<=n;i+=2){ if(i==posi||i+1==posi){ for(int j=1;j<posj;j++){ if(j&1) printf("D"); else printf("U"); printf("R"); } if(posj<m) printf("R"); for(int j=posj+1;j<=m;j++){ if(j&1) printf("U"); else printf("D"); if(j<m) printf("R"); } if(i<n-1) printf("D"); } else if(i<posi){ for(int j=1;j<m;j++) printf("R"); printf("D"); for(int j=1;j<m;j++) printf("L"); if(i<n-1) printf("D"); } else{ for(int j=1;j<m;j++) printf("L"); printf("D"); for(int j=1;j<m;j++) printf("R"); if(i<n-1) printf("D"); } } printf("\n"); } } return 0; }
|