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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

dp专题咯

發布時間:2023/12/18 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 dp专题咯 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

這里會放出一些很有意思的dp或者作者不會的,反正我覺得dp這東西,做多才有經驗的嘛。所以可以看題解呀:
來來來;P2758 編輯距離這題嘛其實看出來是dp但也很難轉移,不過其實多想一想就好了。還是用一下別人的題解:http://blog.csdn.net/CQBZLYTina/article/details/75043128?locationNum=9&fps=1
其實就是從上一個狀態轉移上來的狀態的不同嘛,若是刪除,那就把i-1位變成 j 位子咯,若是插入,其實就是把第j位子刪除嘛,那就和刪除一樣啦,若是修改,那不就是將i-1位變成j-1位?那所有狀態都推出來了啦,

轉移方式有三種,可以為刪除: f[i-1][j]+1,為添加: f[i][j-1]+1 ,為修改f[i-1]f[j-1];
代碼:

#include<bits/stdc++.h> using namespace std; int n,m; int f[2001][2001]; char a[10001],b[10001]; int main() {scanf("%s%s",a,b);int len1=strlen(a),len2=strlen(b);for(int i=1;i<=len1;i++) f[i][0]=i;//初始化,全部刪除 for(int i=1;i<=len2;i++) f[0][i]=i;//嗯 for(int i=1;i<=len1;i++){for(int j=1;j<=len2;j++) {if(a[i-1]==b[j-1]) f[i][j]=f[i-1][j-1];else f[i][j]=min(f[i-1][j-1],min(f[i][j-1],f[i-1][j]))+1;}}printf("%d",f[len1][len2]);return 0;}

還有這一道 P1040 [NOIP2003 提高組] 加分二叉樹(https://www.luogu.com.cn/problem/P1040),就挺離譜啊就題目都沒想明白,一看題解不過如此,哈哈哈哈哈哈哈笑死了:
這個題可以用動態規劃或者記憶化搜索來做。因為如果要求加分最大的話,必須要求它的兒子結點加分最大,所以就有了最優子階段。我們可以枚舉根來更新最大值。中序遍歷有個特點,在中序遍歷這個序列上,某個點左邊的序列一定是這個點的左子樹,右邊的序列,一定在這個點的右子樹。
root[i,j]表示[i,j]這段序列的根,遞歸輸出先序遍歷。注意初始化,f[i][i]=v[i],當序列只有I一個元素時,f[i][i]等于這個點本身的權值,當l==r-1時,此時是空樹設為1。
所見代碼來啦!

#include<bits/stdc++.h> #define int long long using namespace std; int n,m; int a[1000]; int f[50][50]; int r[101][101]; void dfs(int x,int y) {if(x>y) return ;printf("%lld ",r[x][y]);if(x==y) return ;dfs(x,r[x][y]-1);dfs(r[x][y]+1,y);return ; } signed main() {scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lld",&f[i][i]),f[i][i-1]=1,r[i][i]=i;for(int len=1;len<n;len++){for(int i=1;i+len<=n;i++){int j=i+len;f[i][j]=f[i+1][j]+f[i][i];r[i][j]=i;for(int k=i+1;k<j;k++){if(f[i][j]<f[i][k-1]*f[k+1][j]+f[k][k]){f[i][j]=f[i][k-1]*f[k+1][j]+f[k][k];r[i][j]=k;}}}}printf("%lld\n",f[1][n]);dfs(1,n);return 0; }

下一個是P3554 [POI2013]LUK-Triumphal arch,這個是二分加樹上dp。思想是二分答案,然后判斷要提前幫兒子染多少的顏色,注意要舍去小于0的兒子的值。

#include<bits/stdc++.h> using namespace std; int n,m; int len=0,last[310001],son[310001],f[310001]; struct pp {int x,y,next; };pp p[610001]; void ins(int x,int y) {int now=++len;p[now]={x,y,last[x]};last[x]=now;return ; } int getson(int x,int fa) {for(int i=last[x];i!=-1;i=p[i].next){int y=p[i].y;if(y==fa) continue ;son[x]++;getson(y,x);} } void dfs(int x,int fa,int lim) {f[x]=son[x]-lim;for(int i=last[x];i!=-1;i=p[i].next){int y=p[i].y;if(y==fa) continue ;dfs(y,x,lim);if(f[y]>0) f[x]+=f[y];}return ; } int main() {memset(last,-1,sizeof(last));scanf("%d",&n);for(int i=1;i<=n-1;i++){int x,y;scanf("%d%d",&x,&y);ins(x,y);ins(y,x);}getson(1,1);int l=son[1],r=0,ans;for(int i=1;i<=n;i++) r=max(r,son[i]);while(l<=r){int mid=(l+r)/2;dfs(1,1,mid);if(f[1]<=0) r=mid-1,ans=mid;else l=mid+1;}printf("%d",ans);return 0; }

今天比賽有兩個dp不過第一個是有關邊權的排排序就能做了就不放了。對于這種具有單向性的題我們都可以嘗試排序來搞定,第二題是
這個不知道叫什么的dp,時間復雜度是n^2的,枚舉每一個位置,考慮從前面一位轉移,怎么辦呢,考慮最后一位插入j(j<=i,因為到目前為止一共只有i位),在這里引入一個 sum[j],表示為前一輪狀態下,最后一位小于等于 j 的情況的和。那么轉移的話一共會有三種情況,若前面強制小于自己,那么就轉移sum[j-1],若強制大于自己sum[i-1]-sum[j-1],若是無要求就sum[i-1]。代碼:

#include<bits/stdc++.h> using namespace std; int n,m1,m2,mod=1000000007; int p[1000001],f[100001],sum[100001]; int main() { // freopen("candy.in","r",stdin); // freopen("candy.out","w",stdout);memset(p,0,sizeof(p));scanf("%d%d%d",&n,&m1,&m2);for(int i=1;i<=m1;i++) {int x;scanf("%d",&x);p[++x]=1,p[x+1]=2;}for(int i=1;i<=m2;i++) {int x;scanf("%d",&x);p[++x]=2,p[x+1]=1;}sum[0]=0;f[1]=sum[1]=1;for(int i=2;i<=n;i++){for(int j=1;j<=i;j++){if(p[i]==0) f[j]=sum[i-1];else if(p[i]==1) f[j]=(sum[i-1]-sum[j-1]+mod)%mod;else f[j]=sum[j-1];}for(int j=1;j<=i;j++) sum[j]=(sum[j-1]+f[j])%mod;}printf("%d",sum[n]);return 0; }

周末電腦報廢了,所以有些東西沒有寫上來刷了幾道dp的題目
Stones博弈論套dp,記住要枚舉位置,不能枚舉集合中的數,因為是轉移狀態,從前面轉移出來

#include<bits/stdc++.h> using namespace std; int n,m,k,a[100001]; bool v[100001]; int main() {memset(v,false,sizeof(v));scanf("%d%d",&n,&k); for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=k;i++){for(int j=1;j<=n;j++){if(a[j]<=i&&v[i-a[j]]==false) v[i]=true;}}if(v[k]) printf("First\n");else printf("Second\n");return 0; }

Sushi期望dp,具體轉移看別人推的。

//動態規劃優化的初等方法無非就那么幾種,合并狀態就是最重要的思想之一 #include<bits/stdc++.h> using namespace std; int n,m,a[4]; double f[331][331][331]; int main() {scanf("%d",&n);for(int i=1;i<=n;i++) {int x;scanf("%d",&x);a[x]++;} for(int k=0;k<=n;++k){for(int j=0;j<=n;++j){for(int i=0;i<=n;++i){if(i||j||k){if(i)f[i][j][k]+=f[i-1][j][k]*i/(i+j+k);if(j)f[i][j][k]+=f[i+1][j-1][k]*j/(i+j+k);if(k)f[i][j][k]+=f[i][j+1][k-1]*k/(i+j+k);f[i][j][k]+=(double)n/(i+j+k);}}}}printf("%.15lf\n",f[a[1]][a[2]][a[3]]);return 0; }

Coins

//與其算成功的概率,不如將所有情況的可能算出來 //f[i][j]指的是在第i次的時候有j枚朝上 //那這樣就可以從這東西轉移啦,明顯可以滾動優化 //dp的思想便是發現一個問題與上一個問題之間的關系。 #include<bits/stdc++.h> using namespace std; int n,m; double a[10001],f[3001][3001],ans=0; int main() {scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%lf",&a[i]);f[0][0]=1;for(int i=1;i<=n;i++){f[i][0]=f[i-1][0]*(1-a[i]);for(int j=1;j<=i;j++) f[i][j]=f[i-1][j-1]*a[i]+f[i-1][j]*(1-a[i]);}for(int i=1;i<=n;i++){if(i>n-i) ans+=f[n][i];}printf("%.10lf",ans);return 0; }

LCS這一題的話,是一個很典型的題目, f[i][j]=max(f[i-1][j],f[i][j-1]);以這種方式向前轉移,就是自己與前面的最佳狀態比較,若沒記錯,倍增里面的題也有用到這種思路的。

#include<bits/stdc++.h> using namespace std; int n,m,len1,len2; char s1[3021],s2[3021],ans[3021]; int f[3001][3001]; int main() {scanf("%s%s",s1+1,s2+1);len1=strlen(s1+1);len2=strlen(s2+1);for(int i=1;i<=len1;i++){for(int j=1;j<=len2;j++) {f[i][j]=max(f[i-1][j],f[i][j-1]);if(s1[i]==s2[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1); }}int i=len1,j=len2;//printf("*");while(f[i][j]!=0){if(s1[i]==s2[j]) ans[f[i][j]]=s1[i],i--,j--;else {if(f[i-1][j]==f[i][j]) i--;else j--;}}printf("%s",ans+1);return 0; }

Knapsack 2這個是新型背包,也不算吧,只不過思路新些

//轉換思維,當價值取i時讓重量最小 //最后枚舉價值取重量即可,所以我們枚舉價值(10^6) #include<bits/stdc++.h> using namespace std; int n,m,maxx,v[100001],w[100001],f[2000001]; int main() {memset(f,63,sizeof(f));f[0]=0;scanf("%d%d",&n,&m);maxx=n*1000;for(int i=1;i<=n;i++){scanf("%d%d",&w[i],&v[i]);for(int j=maxx;j>=v[i];j--) f[j]=min(f[j],f[j-v[i]]+w[i]);}for(int i=maxx;i>=0;i--){if(f[i]<=m) {printf("%d",i);return 0;}}return 0; }

Deque區間dp,考慮轉移,分類討論。

//考慮做一個區間dp,枚舉區間的長度,再枚舉左端點,計算出右端點 //那么討論發現,若取走的數有奇數個,那么是后手取,若是偶數個,那是先手取 //然后直接轉移便可 具體討論不告訴你。 #include<bits/stdc++.h> using namespace std; int n,m,a[4001],f[4001][4001]; int main() {scanf("%d",&n);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int len=1;len<=n;len++){for(int i=1;i+len-1<=n;i++){int j=i+len-1; if((n-len)%2==1) f[i][j]=min(f[i+1][j]-a[i],f[i][j-1]-a[j]);else f[i][j]=max(f[i+1][j]+a[i],f[i][j-1]+a[j]);} } printf("%d",f[1][n]);return 0; }

也是區間dpSlimes,考慮每一個區間如何轉移,那么是(左+右+合并代價)前兩者可以從前面轉移,后者可以計算得出。

#include<bits/stdc++.h> using namespace std; long long n,m,f[1001][1001],a[10001]; int main() {memset(f,63,sizeof(f));scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lld",&a[i]),f[i][i]=0;for(int len=2;len<=n;len++){for(int i=1;i+len-1<=n;i++){int j=i+len-1;for(int k=i;k<j;k++) f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);//枚舉斷點for(int k=i;k<=j;k++) f[i][j]+=a[k];//加上合并的代價 }}printf("%lld",f[1][n]);return 0; }

Candies這一題

//dp首先考慮枚舉位置及那個位置小朋友得到的糖 ,再考慮如何從前面轉移,呃那么就要枚舉前面的小朋友的位置,時間O(n*n*k) //考慮優化,不妨用前綴和,記sum[i][j]=f[i][k](1 <= k <= j) 那么便是(nk)的轉移了 //聽別人說還有一個向后轉移用差分 #include<bits/stdc++.h> #define int long long using namespace std; int n,m,mod=1e9+7; int a[200001],sum[200][200001],f[200][200001]; signed main() {scanf("%lld%lld",&n,&m);f[0][0]=sum[0][0]=1;for(int i=1;i<=n;i++) scanf("%lld",&a[i]);for(int i=1;i<=m;i++) sum[0][i]+=sum[0][i-1];for(int i=1;i<=n;i++){for(int j=0;j<=m;j++){int l=max(0ll,j-a[i]),r=j;f[i][j]=((f[i][j]+sum[i-1][r])%mod-sum[i-1][l-1]+mod)%mod;} sum[i][0]=1;for(int j=1;j<=m;j++) sum[i][j]=(sum[i][j-1]+f[i][j]+mod)%mod;}printf("%lld",f[n][m]);return 0; }

Independent Set神奇的樹形dp。

#include<bits/stdc++.h> #define int long long using namespace std; int n,m,len=0,last[200001],f[200001][3],mod=1e9+7;//0表示的是黑色,1表示的白色 //說真的還是白色受我喜歡些吧!變態吧! struct pp {int x,y,next; };pp p[400001]; void ins(int x,int y) {int now=++len;p[now]={x,y,last[x]};last[x]=now;return ; } void dfs(int x,int fa) {f[x][0]=f[x][1]=1;for(int i=last[x];i!=-1;i=p[i].next){int y=p[i].y;if(y==fa) continue ;dfs(y,x);f[x][0]*=f[y][1];f[x][1]*=(f[y][0]+f[y][1]);f[x][0]%=mod;f[x][1]%=mod; }return ; } signed main() {memset(last,-1,sizeof(last));scanf("%lld",&n);for(int i=1;i<=n-1;i++){int x,y;scanf("%lld%lld",&x,&y);ins(x,y);ins(y,x);}dfs(1,1);printf("%lld",(f[1][0]+f[1][1])%mod);return 0; }

Flowers,呃不算難,拿樹狀數組維護。從前一個最大值的位置轉移至目前這個。

//很強的樹狀數組思路,每次找到前面的最大值后,加上自己的值后插入自己的位置。 //那么其實就可以達到一個類似于前綴和的效果 #include<bits/stdc++.h> #define int long long #define lowbit(x) x&(-x) using namespace std; int n,m,h[210000],f[410000],ans=0; void cg(int x,int k) {for(;x<=n;x+=lowbit(x)) f[x]=max(f[x],k);return ; } int findsum(int x) {int rt=0;for(x;x>=1;x-=lowbit(x)) rt=max(f[x],rt);return rt; } signed main() {scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lld",&h[i]);for(int i=1;i<=n;i++) {int x,k;scanf("%lld",&x);k=findsum(h[i]-1)+x;ans=max(ans,k);cg(h[i],k);}printf("%lld",ans);return 0; }

Intervals那個考慮轉移,從不同的狀態轉移過來,那么就是。(最好看看題解)

這樣但是O(n^3)的轉移。

具體如何維護呢對。把 dp 數組扔到線段樹上啊,然后直接像取最大值,區間加一樣維護就行了,再具體的看代碼吧。還有就是我們按右端點來計數,

//先做成 O(n^3)的dp,在第i個位置,前一個0在第j個位置,發現有兩種狀態的轉移 //具體看上面,然后我們可以用滾動數組壓一維,但轉移還是不能接受,怎么辦呢。 //把這東西扔到線段樹上,維護區間的最大值,以及搞一個區間加,每次查詢其實就是1到n的最大值啦 #include<bits/stdc++.h> #define int long long using namespace std; int n,m,len=0; struct node {int l,r,lc,rc,maxx,lazy; };node e[4000001]; struct node1 {int l,r,val; };node1 a[2000001]; bool cmp(const node1 &x,const node1 &y) {return x.r<y.r; } int bt(int x,int y) {int now=++len;int l=x,r=y,lc=-1,rc=-1,maxx=0;if(x==y) ;else {int mid=(l+r)/2;lc=bt(l,mid);rc=bt(mid+1,r);}e[now]={l,r,lc,rc,maxx,0};return now; } void pushdown(int now) {if(e[now].lazy==0) return ;int lc=e[now].lc,rc=e[now].rc,k=e[now].lazy;e[now].lazy=0;e[lc].lazy+=k;e[rc].lazy+=k;e[lc].maxx+=k;e[rc].maxx+=k;return ; } void add(int now,int x,int y,int k) {int l=e[now].l,r=e[now].r;if(l==x&&y==r) e[now].maxx+=k,e[now].lazy+=k;else {int lc=e[now].lc,rc=e[now].rc,mid=(l+r)/2;pushdown(now);if(x>=mid+1) add(rc,x,y,k);else if(y<=mid) add(lc,x,y,k);else add(lc,x,mid,k),add(rc,mid+1,y,k);e[now].maxx=max(e[lc].maxx,e[rc].maxx); } return ; } signed main() {scanf("%lld%lld",&n,&m);for(int i=1;i<=m;i++) scanf("%lld%lld%lld",&a[i].l,&a[i].r,&a[i].val);int root=bt(1,n);sort(a+1,a+m+1,cmp);for(int i=1,now=1;i<=n;i++){add(root,i,i,max(0ll,e[root].maxx));while(a[now].r==i) add(root,a[now].l,i,a[now].val),now++; } printf("%lld",max(0ll,e[root].maxx));return 0; }

同樣一道dp轉移題,采用的是前綴和優化Permutation,轉移方式和前面的前綴和轉移也都差不多

#include<bits/stdc++.h> #define int long long using namespace std; int n,m,f[3010][3010],sum[3010],mod=1e9+7; char s[10001]; main() {scanf("%lld%s",&n,s+2);f[1][1]=1;for(int i=2;i<=n;i++){memset(sum,0,sizeof(sum));for(int j=1;j<=i-1;j++) sum[j]=sum[j-1]+f[i-1][j];for(int j=1;j<=i;j++){if(s[i]=='<') f[i][j]=(f[i][j]+sum[j-1]-sum[0])%mod;else f[i][j]=(f[i][j]+sum[i-1]-sum[j-1])%mod; }}int ans=0;for(int i=1;i<=n;i++) ans=(ans+f[n][i])%mod;printf("%lld",ans);return 0; }

Subtree考慮樹形dp,但是發現維護困難,(n^2),考慮換根。嗯。

//對于一個點now,你需要分兩類,即 now 子樹里(無now的)和除了 now 的子樹(含 now); //這樣說太麻煩,感性理解為取黑色節點時向上走呢,還是向下走,最終兩種情況的和乘起來即可,當然取法還是有說法的,不表。 //考慮維護,枚舉每一個節點的話會時超,想到換根,那通過什么來轉移呢。用前綴和,但是僅僅是和是無法轉移的, //因為對于每一個子節點,要乘上在他前面的所有兄弟才成 //那么就還有多一個的后綴積,然后取模的話其實用不著逆元,直接搞就行 #include<bits/stdc++.h> #define int long long using namespace std; int n,m,len=0,last[3000001],mod; struct pp {int x,y,next; };pp p[3000001]; int qx[3000001],hx[3000001],f[3000001],ans[3000001]; void ins(int x,int y) {int now=++len;p[now]={x,y,last[x]};last[x]=now;return ; } int getsum(int i,int fa,int val) {if(i==-1) return 1;int y=p[i].y;qx[y]=val;if(y==fa) return hx[y]=getsum(p[i].next,fa,val)%mod;hx[y]=getsum(p[i].next,fa,(val*(f[y]+1))%mod);return (hx[y]*(f[y]+1))%mod; } void dfsdp(int x,int fa) {f[x]=1;for(int i=last[x];i!=-1;i=p[i].next){int y=p[i].y;if(y==fa) continue ;dfsdp(y,x);f[x]=(f[x]*(f[y]+1))%mod;}getsum(last[x],fa,1);return ; } void hg(int x,int fa,int ff) {if(x!=1) {ff=((ff+1)*(qx[x]*hx[x]%mod)%mod)%mod;ans[x]=(f[x]*(ff+1))%mod; }for(int i=last[x];i!=-1;i=p[i].next){int y=p[i].y;if(y==fa) continue ;hg(y,x,ff);}return ;} signed main() {memset(last,-1,sizeof(last));scanf("%lld%lld",&n,&mod);for(int i=1;i<=n-1;i++){int x,y;scanf("%lld%lld",&x,&y);ins(x,y);ins(y,x); }dfsdp(1,1);ans[1]=f[1]%mod;hg(1,1,0);for(int i=1;i<=n;i++) printf("%lld\n",ans[i]%mod);return 0; }

一道貪心+dpTower
這里給出別人的貪心證明

//明顯的01背包,問題在于如何維護 ,推導一下,先后順序的話應該是 s[x]-w[y]>s[y]-w[x] //為何?因為在此時先放x一定優于先放y,那么直接以此為關鍵字跑一次就行 #include<bits/stdc++.h> #define int long long using namespace std; int n,m,f[200001],V=2e4,ans; struct node {int s,w,val; };node a[200001]; bool cmp(const node &x,const node &y) {return x.s+x.w<y.s+y.w; } signed main() {scanf("%lld",&n);for(int i=1;i<=n;i++) scanf("%lld%lld%lld",&a[i].w,&a[i].s,&a[i].val);sort(a+1,a+n+1,cmp);for(int i=1;i<=n;i++){for(int j=min(a[i].s+a[i].w,V);j>=a[i].w;j--) f[j]=max(f[j],f[j-a[i].w]+a[i].val); } for(int i=0;i<=V;i++) ans=max(ans,f[i]);printf("%lld",ans);return 0; }

一道不知名dp題:Grid 2
)這一題的話其實看出來就不難,提幾個點,第一個是從(1,1)到(ai,bi)這一個點一共要走ai+bi-2步,所以它的貢獻是在1到ai,bi這些點中選擇ai+bi-2那么多個點(好累,寫的不好看別人的去)。

#include<bits/stdc++.h> #define int long long using namespace std; int n,m,k,mod=1e9+7; int fact[200005],inv[200005],finv[200005],f[3005];//fact階乘,inv逆元,finv階乘的逆元 struct node {int x,y; };node a[1000001]; bool cmp(const node &x,const node &y) {if(x.x!=y.x) return x.x<y.x;return x.y<y.y; } int C(int x,int y) {return fact[x]*finv[y]%mod*finv[x-y]%mod;//分開兩次取模,我快 樂死了 } main() {scanf("%lld%lld%lld",&n,&m,&k);fact[0]=fact[1]=inv[1]=finv[0]=finv[1]=1ll;for(int i=2;i<=n+m;i++) {fact[i]=fact[i-1]*i%mod;inv[i]=(mod-mod/i)*inv[mod%i]%mod;finv[i]=finv[i-1]*inv[i]%mod;}for(int i=1;i<=k;i++) scanf("%lld%lld",&a[i].x,&a[i].y),a[i].x--,a[i].y--;sort(a+1,a+k+1,cmp);a[++k].x=n-1;a[k].y=m-1;for(int i=2;i<=k;i++){for(int j=1;j<i;j++) {if(a[j].x<=a[i].x&&a[j].y<=a[i].y) {f[i]=(f[i]+(C(a[j].x+a[j].y,a[j].x)+mod-f[j])%mod*C(a[i].x-a[j].x+a[i].y-a[j].y,a[i].x-a[j].x)%mod)%mod;//做}}} printf("%lld\n",(C(n+m-2,n-1)-f[k]+mod)%mod);return 0; }

P6475 [NOI Online #2 入門組] 建設城市,呃啊,一個不錯的dp,討論xy在同側或者異側的情況。放別人的題解:

#include<bits/stdc++.h> #define int long long using namespace std; int n,m,mod=998244353,ans=0; int fc[2550001],inv[2550001]; int ksm(int x,int k) {int rt=1;while(k){if(k&1) rt=rt*x%mod;x=x*x%mod;k>>=1;}return rt; } int C(int x,int y) {return fc[x+y-1]*inv[y]%mod*inv[x-1]%mod; } signed main() {int x,y;scanf("%lld%lld%lld%lld",&m,&n,&x,&y);fc[0]=1;for(int i=1;i<=m+n;i++) fc[i]=fc[i-1]*i%mod;inv[n+m]=ksm(fc[n+m],mod-2); for(int i=m+n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod;if(x<=n&&y>n) {for(int i=1;i<=m;i++) ans=(ans+C(i,x-1)*C(m-i+1,n-x)%mod*C(m-i+1,y-n-1)%mod*C(i,2*n-y))%mod;}else ans=C(m,n)*C(m,n+x-y)%mod;printf("%lld",ans);return 0; }

Matching我來補題啦。狀壓dp耶,好欸你看n<=21,那么一眼看出壓法的話是向前枚舉。

#include<bits/stdc++.h> using namespace std; int n,m,f[1<<21+1],mod=1e9+7,a[52][52],nn; int gettot(int x) {int tot=0;while(x) tot+=(x&1),x>>=1;return tot; } int main() {scanf("%d",&n);nn=(1<<n)-1;for(int i=0;i<n;i++) {for(int j=0;j<n;j++) scanf("%d",&a[i][j]);}f[0]=1;for(int i=0;i<=nn;i++){int sum=gettot(i);for(int j=0;j<n;j++){ if(!(i&(1<<j))&&a[sum][j]) (f[i|(1<<j)]+=f[i])%=mod;}}printf("%d",f[nn]);return 0; }

總結

以上是生活随笔為你收集整理的dp专题咯的全部內容,希望文章能夠幫你解決所遇到的問題。

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