日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

[USACO]Sprinklers 2: Return of the Alfalfa P(网格DP)

發布時間:2023/12/3 编程问答 49 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [USACO]Sprinklers 2: Return of the Alfalfa P(网格DP) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

思路:
由題目易得:網格內種植的兩種植物形成了一條 左上—右下 的分界線,考慮將問題轉化成 DP求出有多少條合法的分界線

我們注意到:
分界線上的點都必須放灑水器,且所放灑水器有唯一選擇;
其他的可以放灑水器的點則都有2種選擇(放或不放)。
所以,總方案數=合法分界線條數*(2∧網格中可放灑水器的點的數量)/(2∧分界線上折點數量)

[圖中黃線為分界線,灰格子代表該位置可以放也可以不放灑水器,橙格子代表該位置放甜玉米灑水器,藍格子代表放紫苜蓿灑水器]

DP求合法分界線條數:
設 f(i,j) 表示已經考慮好了前 j 列的分界線,第 j 列的分界線在第 i 行。(注意這里的 i 可以到 n+1)

轉移時,我們枚舉格子(i-1,k)(1<=k<=j),看上面有沒有奶牛,
若沒有奶牛:

因為這里增加了2個折點,所以除以 4。(注意邊界情況下只用除以 2)

以下圖為例,是考慮將黃線作為分界線的情況:

我們需要枚舉的是a、b、c、d四個格子

枚舉到d時,d格子上沒有奶牛,因此d格子上可以放紫苜蓿灑水器
所以,黃線①或②可以與新添黃線構成一條合法分界線
由此一來,新增了藍色的2個折點(邊界情況下新增1個折點)

枚舉到c時,由于c上不能放灑水器,所以藍色交叉所在點不能成為分界線的折點,因此這種情況下 f(i,j) 沒有變化。

枚舉a、b的情況略
考慮邊界:

圖中明黃色折線和暗黃色直線均為 分界線可能的一部分
(明黃色折線可向左、下兩邊延伸,暗黃色直線也可沿指示方向延伸)

以下是對邊界情況的處理(主要考慮 初始化 和 新增折點個數 的問題):

f[1][0]=1;for(int j=1;j<=n;j++)if(g[1][j]=='.') f[1][j]=1*inv2; for(int k=1;k<=n;k++){if(g[n][k]=='.'){for(int l=1;l<=n;l++)f[n+1][n]=(f[n+1][n]+f[l][k-1])%mod;} } f[n+1][n]=f[n+1][n]*inv2%mod;

前綴和優化:
先放一段優化前的代碼:

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int mod=1e9+7; const int N=2010; int inv2,inv4; int n,s=0; char g[N][N]; ll f[N][N],sum[N]; ll ans; int power(ll a,ll b){ int ret=1;a=a%mod; while(b>0){ if(b%2==1) ret=ret*a%mod; b/=2; a=a*a%mod; } return ret; } int main(){inv2=power(2,mod-2);inv4=power(2,mod-3);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",g[i]+1);for(int j=1;j<=n;j++)s+=g[i][j]=='.';} f[1][0]=1;for(int j=1;j<=n;j++)if(g[1][j]=='.') f[1][j]=1*inv2;for(int i=2;i<=n;i++){for(int j=1;j<=n;j++){if(g[i][j]=='W') continue;for(int k=1;k<=j;k++){if(g[i-1][k]=='.'){for(int l=1;l<=i-1;l++)f[i][j]=(f[i][j]+f[l][k-1])%mod;} }f[i][j]=f[i][j]*inv4%mod;}}for(int k=1;k<=n;k++){if(g[n][k]=='.'){for(int l=1;l<=n;l++)f[n+1][n]=(f[n+1][n]+f[l][k-1])%mod;} }f[n+1][n]=f[n+1][n]*inv2%mod;for(int i=1;i<=n+1;i++) ans+=f[i][n];ans=ans*power(2,s)%mod;printf("%lld\n",ans);return 0; }

考慮設sum[i][j]=f[1][j]+f[2][j]+…+f[i][j],代碼變為:

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int mod=1e9+7; const int N=2010; int inv2,inv4; int n,s=0; char g[N][N]; ll f[N][N],sum[N][N]; ll ans; int power(ll a,ll b){ int ret=1;a=a%mod; while(b>0){ if(b%2==1) ret=ret*a%mod; b/=2; a=a*a%mod; } return ret; } int main(){inv2=power(2,mod-2);inv4=power(2,mod-3);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",g[i]+1);for(int j=1;j<=n;j++)s+=g[i][j]=='.';} f[1][0]=1;for(int i=1;i<=n;i++) sum[i][0]=1;for(int j=1;j<=n;j++)if(g[1][j]=='.') sum[1][j]=f[1][j]=1*inv2;for(int i=2;i<=n;i++){for(int j=1;j<=n;j++){sum[i][j]=sum[i-1][j];if(g[i][j]=='W') continue;for(int k=1;k<=j;k++){if(g[i-1][k]=='.')f[i][j]=(f[i][j]+sum[i-1][k-1])%mod;}f[i][j]=f[i][j]*inv4%mod;sum[i][j]+=f[i][j],sum[i][j]%=mod;}}for(int k=1;k<=n;k++){if(g[n][k]=='.')f[n+1][n]=(f[n+1][n]+sum[n][k-1])%mod;}f[n+1][n]=f[n+1][n]*inv2%mod;for(int i=1;i<=n+1;i++) ans+=f[i][n];ans=ans*power(2,s)%mod;printf("%lld\n",ans);return 0; }

然后,我們高興地發現還可以繼續優化:
最終代碼:

#include<iostream> #include<cstdio> using namespace std; typedef long long ll; const int mod=1e9+7; const int N=2010; int inv2,inv4; int n,s=0; char g[N][N]; ll f[N][N],sum[N][N],f1[N][N]; ll ans; int power(ll a,ll b){ int ret=1;a=a%mod; while(b>0){ if(b%2==1) ret=ret*a%mod; b/=2; a=a*a%mod; } return ret; } int main(){inv2=power(2,mod-2);inv4=power(2,mod-3);scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%s",g[i]+1);for(int j=1;j<=n;j++)s+=g[i][j]=='.';} f[1][0]=f1[1][0]=1;for(int i=1;i<=n;i++) sum[i][0]=1;for(int j=1;j<=n;j++){if(g[1][j]=='.') sum[1][j]=f[1][j]=1*inv2;f1[1][j]=1*inv2;} for(int i=2;i<=n;i++){for(int j=1;j<=n;j++){f1[i][j]=f1[i][j-1]; if(g[i-1][j]=='.')f1[i][j]=(f1[i][j]+sum[i-1][j-1]*inv4%mod)%mod;if(g[i][j]=='W') f[i][j]=0;else f[i][j]=f1[i][j];sum[i][j]=(sum[i-1][j]+f[i][j])%mod;}}for(int k=1;k<=n;k++){if(g[n][k]=='.')f[n+1][n]=(f[n+1][n]+sum[n][k-1])%mod;}f[n+1][n]=f[n+1][n]*inv2%mod;sum[n+1][n]=(sum[n][n]+f[n+1][n])%mod;printf("%lld\n",sum[n+1][n]*power(2,s)%mod);return 0; }

總結

以上是生活随笔為你收集整理的[USACO]Sprinklers 2: Return of the Alfalfa P(网格DP)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。