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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

动态规划复习

發(fā)布時(shí)間:2025/3/14 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 动态规划复习 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

發(fā)現(xiàn)一個(gè)整理dp基礎(chǔ)方程整理得挺好的博客:http://www.cnblogs.com/keshuqi/p/7715167.html

發(fā)現(xiàn)一個(gè)總結(jié)多叉樹樹形背包常見建模方法的博客:http://blog.csdn.net/no1_terminator/article/details/77824790

?

最近除了模擬賽和往年noip題自我測(cè)試,就只能搞點(diǎn)弱項(xiàng)專題訓(xùn)練了。

都是洛谷上的題,每次從水題開始:

?

便宜的回文:

區(qū)間dp,對(duì)于一個(gè)字母,增刪其實(shí)效果是相同的,取代價(jià)最小的即可。

//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=3000+10; int n,m,val[maxn],dp[maxn][maxn]; char c,s[maxn];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {m=read();n=read(); int x,y;scanf("%s",s+1);for(int i=1;i<=m;++i) {do c=getchar();while(c<'a'||c>'z');x=c-'a'+1; y=min(read(),read());val[x]=y;}memset(dp,0x3f3f3f3f,sizeof(dp));for(int i=1;i<=n;++i) dp[i][i]=0;for(int i=1;i<n;++i) if(s[i]==s[i+1]) dp[i][i+1]=0;for(int l=1;l<n;++l) {for(int i=1;i<=n-l;++i) {int j=i+l;if(s[i]==s[j]&&l>1) dp[i][j]=min(dp[i][j],dp[i+1][j-1]);dp[i][j]=min(dp[i][j],dp[i+1][j]+val[s[i]-'a'+1]);dp[i][j]=min(dp[i][j],dp[i][j-1]+val[s[j]-'a'+1]);}}printf("%d",dp[1][n]);return 0; }

  

?道路游戲:

這是一道其實(shí)只有普及+/提高-的題,因?yàn)槿娇蛇^。

三方的代碼:

int get_sum(int r,int pos,int t) {
  pos=(pos-r-1+n*m)%n+1;
  return sum[pos][r+t]-sum[pos][r];
}

int main() {
  n=read();m=read();p=read();
  for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) tu[i][j]=read();
  for(int i=1;i<=n;++i) cost[i]=read();
  for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sum[i][j]=sum[i][j-1]+tu[(i+j-2)%n+1][j];
  for(int i=1;i<=m;++i) dp[i]=-INF;
  for(int i=0;i<m;++i) for(int j=1;j<=n;++j)
    for(int k=1;k<=p&&k+i<=m;++k) dp[i+k]=max(dp[i+k],dp[i]+get_sum(i,j,k)-cost[j]);
  printf("%d",dp[m]);
  return 0;
}

肯定是平方的解決方式要好一點(diǎn)啊,此題用三方做有什么意義呢?但是我太菜不會(huì)平方啊。。。

?

奶牛浴場(chǎng):

這道題似乎哪里見過。似乎是做過的?

一看那個(gè)n是可以平方的,l、w是不行的,那肯定是n平方的嘍。

情況比較多,需要都考慮到,但是數(shù)據(jù)比較水我也沒有辦法。

借用洛谷上?I_AM_HelloWord?的題解(侵刪):

先枚舉極大子矩形的左邊界,然后從左到右依次掃描每一個(gè)障礙點(diǎn),并不斷修改可行的上下邊界,從而枚舉出所有以這個(gè)定點(diǎn)為左邊界的極大子矩形。考慮如圖2中的三個(gè)點(diǎn),現(xiàn)在我們要確定所有以1號(hào)點(diǎn)為左邊界的極大矩形。先將1號(hào)點(diǎn)右邊的點(diǎn)按橫坐標(biāo)排序。然后按從左到右的順序依次掃描1號(hào)點(diǎn)右邊的點(diǎn),同時(shí)記錄下當(dāng)前的可行的上下邊界。

開始時(shí)令當(dāng)前的上下邊界分別為整個(gè)矩形的上下邊界。然后開始掃描。第一次遇到2號(hào)點(diǎn),以2號(hào)點(diǎn)作為右邊界,結(jié)合當(dāng)前的上下邊界,就得到一個(gè)極大子矩形(如圖3)。

同時(shí),由于所求矩形不能包含2號(hào)點(diǎn),且2號(hào)點(diǎn)在1號(hào)點(diǎn)的下方,所以需要修改當(dāng)前的下邊界,即以2號(hào)點(diǎn)的縱坐標(biāo)作為新的下邊界。第二次遇到3號(hào)點(diǎn),這時(shí)以3號(hào)點(diǎn)的橫坐標(biāo)作為右邊界又可以得到一個(gè)滿足性質(zhì)1的矩形(如圖4)。

類似的,需要相應(yīng)地修改上邊界。以此類推,如果這個(gè)點(diǎn)是在當(dāng)前點(diǎn)(確定左邊界的點(diǎn))上方,則修改上邊界;如果在下方,則修改下邊界;如果處在同一行,則可中止搜索(因?yàn)楹竺娴木匦蚊娣e都是0了)。由于已經(jīng)在障礙點(diǎn)集合中增加了整個(gè)矩形右上角和右下角的兩個(gè)點(diǎn),所以不會(huì)遺漏右邊界與整個(gè)矩形的右邊重合的極大子矩形(如圖5)。

需要注意的是,如果掃描到的點(diǎn)不在當(dāng)前的上下邊界內(nèi),那么就不需要對(duì)這個(gè)點(diǎn)進(jìn)行處理。

這樣做是否將所有的極大子矩形都枚舉過了呢?

可以發(fā)現(xiàn),這樣做只考慮到了左邊界覆蓋一個(gè)點(diǎn)的矩形,因此我們還需要枚舉左邊界與整個(gè)矩形的左邊界重合的情況。這還可以分為兩類情況。一種是左邊界與整個(gè)舉行的左邊界重合,而右邊界覆蓋了一個(gè)障礙點(diǎn)的情況,對(duì)于這種情況,可以用類似的方法從右到左掃描每一個(gè)點(diǎn)作為右邊界的情況。

另一種是左右邊界均與整個(gè)矩形的左右邊界重合的情況,對(duì)于這類情況我們可以在預(yù)處理中完成:先將所有點(diǎn)按縱坐標(biāo)排序,然后可以得到以相鄰兩個(gè)點(diǎn)的縱坐標(biāo)為上下邊界,左右邊界與整個(gè)矩形的左右邊界重合的矩形,顯然這樣的矩形也是極大子矩形,因此也需要被枚舉到。

//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=5000+10; int l,w,n,ans;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }struct Node{int x,y; }node[maxn];bool cmp(const Node& a,const Node& b) {return a.x < b.x; }bool cmp2(const Node& a,const Node& b) {return a.y < b.y; }int main() {l=read();w=read();n=read();for(int i=1;i<=n;++i) {node[i].x=read();node[i].y=read(); }node[++n].x=0;node[n].y=0;node[++n].x=l;node[n].y=0;node[++n].x=0;node[n].y=w;node[++n].x=l;node[n].y=w;sort(node+1,node+n+1,cmp);int xx,yy;for(int i=1;i<=n;++i) {xx=0;yy=w; if(i!=1) ans=max(ans,(node[i].x-node[i-1].x)*w);for(int j=i+1;j<=n;++j) {ans=max(ans,(node[j].x-node[i].x)*(yy-xx));if(node[j].y<=xx||node[j].y>=yy) continue;if(node[j].y<node[i].y) xx=node[j].y;else if(node[j].y>node[i].y) yy=node[j].y;else break;}xx=0;yy=w;for(int j=i-1;j;--j) {ans=max(ans,(node[i].x-node[j].x*(yy-xx)));if(node[j].y<=xx||node[j].y>=yy) continue;if(node[j].y<node[i].y) xx=node[j].y;else if(node[j].y>node[i].y) yy=node[j].y;else break;}}sort(node+1,node+n+1,cmp2);for(int i=1;i<n;++i) ans=max(ans,(node[i+1].y-node[i].y)*l);printf("%d",ans);return 0; }

  

翻轉(zhuǎn)棋:

為什么感覺都做過呢,套路啊。。。

我們知道,一個(gè)格子要么翻一次要么不翻,我們發(fā)現(xiàn)如果第一行的格子翻或不翻的狀態(tài)定了,那么所有格子的狀態(tài)就定了。

由于n、m只有15,我們就直接枚舉第一行格子的狀態(tài)然后直接算出后面格子的狀態(tài)。

//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=15+2,maxs=1<<15|2,INF=1e7; int n,m,now,nowans,ans=INF,ff[maxn]; bool ok,ykk; bool tu[maxn][maxn],num[maxn][maxn];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }void get_num(int p) {nowans=0;for(int i=m;i;--i) {nowans+=(num[1][i]=(p&1));p>>=1;} }int get_now(int x,int y) {int rs=tu[x][y]^num[x][y];if(x>1) rs^=num[x-1][y];if(x<n) rs^=num[x+1][y];if(y>1) rs^=num[x][y-1];if(y<m) rs^=num[x][y+1];return rs; }void print_ans() {memset(ff,0,sizeof(ff));for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) if(num[i][j]) ff[i]|=(1<<j-1);ans=nowans; }int main() {n=read(); m=read();for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) tu[i][j]=read();for(int p=0;p<(1<<m);++p) {memset(num,0,sizeof(num)); get_num(p); ok=0;for(int i=2;i<=n&&nowans<ans;++i) for(int j=1;j<=m;++j) {now=get_now(i-1,j);if(now) nowans+=(num[i][j]=1);}for(int i=1;i<=m;++i) if(get_now(n,i)) {ok=1; break;}if(!ok&&nowans<ans) print_ans();}if(ans==INF) printf("IMPOSSIBLE\n");else {for(int i=1;i<=n;++i) {for(int j=1;j<=m;++j) {printf("%d ",ff[i]&1);ff[i]>>=1;}printf("\n");}}return 0; }

  

多人背包:

求前k優(yōu)解的和。

剛開始看到這個(gè)題的時(shí)候覺得非常不可做。

然后就想了一會(huì)(反正腦子也不清醒也想不到什么)就看題解了,然后題解說歸并排序,秒懂。

假如dp[i][j][t]表示目前取到第i件物品,消耗體積j,的第t優(yōu)解。

那么它一定由dp[i-1][j]、dp[i-1][j-v[i]]轉(zhuǎn)移過來,直接把這兩個(gè)里面前k個(gè)最優(yōu)的歸并弄出來就可以了。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxv=5000+10,maxk=50+5,maxn=200+10,INF=1e8; int k,v,n,f[maxv][maxk],g[maxv][maxk],p[maxn],w[maxn],ans;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {k=read();v=read();n=read();for(int i=1;i<=n;++i) {p[i]=read();w[i]=read();}for(int i=0;i<=v;++i) for(int t=1;t<=k;++t) g[i][t]=-INF;g[0][1]=0;for(int i=1;i<=n;++i) {for(int j=0;j<=v;++j) {if(j<p[i]) for(int t=1;t<=k;++t) f[j][t]=g[j][t];else {int pos1=1,pos2=1;for(int t=1;t<=k;++t) {if(pos2>k||(pos1<=k&&g[j][pos1]>=g[j-p[i]][pos2]+w[i])) f[j][t]=g[j][pos1++];else f[j][t]=g[j-p[i]][pos2++]+w[i];}}}memcpy(g,f,sizeof(f));memset(f,0,sizeof(f));}for(int i=1;i<=k;++i) ans+=g[v][i];printf("%d",ans);return 0; }

  

二叉蘋果樹:

一道樹dp裸題,但是一直沒有過,弄了很久發(fā)現(xiàn)是一個(gè)地方z寫成了y。

//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=100+10,INF=1e6; int n,m,dp[maxn][maxn],sum;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int fir[maxn],to[2*maxn],nxt[2*maxn],e=0,v[2*maxn]; void add(int x,int y,int z) {to[++e]=y;nxt[e]=fir[x];fir[x]=e;v[e]=z;to[++e]=x;nxt[e]=fir[y];fir[y]=e;v[e]=z; }int g[maxn],size[maxn],tot[maxn]; void dfs(int pos,int f) {int y,z; size[pos]=1;dp[pos][0]=0;for(y=fir[pos];y;y=nxt[y]) {if(f==(z=to[y])) continue;g[z]=v[y]; dfs(z,pos); size[pos]+=size[z]; g[pos]+=g[z];for(int i=m;~i;--i) {for(int j=0;j<=min(i,size[z]-1);++j) dp[pos][i]=min(dp[pos][i],dp[z][j]+dp[pos][i-j]);if(i>=size[z]) dp[pos][i]=min(dp[pos][i],dp[pos][i-size[z]]+g[z]);}} }int main() {n=read();m=n-1-read();int x,y,z;memset(dp,0x3f3f3f3f,sizeof(dp));for(int i=1;i<n;++i) {x=read();y=read();z=read();sum+=z; add(x,y,z);}dfs(1,0);printf("%d",sum-dp[1][m]);return 0; }

  

硬幣的游戲A Coin Game:

這道題讓我想起了一道題,但是記不到那道題具體是什么了,只記得當(dāng)時(shí)沒有看懂題目,主要是沒有理解兩個(gè)人都足夠聰明是什么意思。

感覺這道題這句話是一樣的道理:兩個(gè)玩家都希望拿到最多錢數(shù)的硬幣。

希望是希望,不一定能成功,但是題目的意思就是兩個(gè)人都是最優(yōu)策略。

A希望取價(jià)值和最大,相當(dāng)于他想讓B取價(jià)值和最小,但是在A取后B一定會(huì)使用最優(yōu)策略,所以A要使B最優(yōu)策略最劣。

然后就只能倒著做了。dp[i][j]表示取第i個(gè)的人在這一次取j個(gè)硬幣可以得到的最大價(jià)值,為了簡(jiǎn)化轉(zhuǎn)移,我們dp表示一個(gè)前綴最大價(jià)值來搞:

dp[i][j]=max(dp[i][j-1],w[i]-dp[i+j][min(n-i-j+1,2*j)]);

//Serene #include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=2000+10; int n,w[maxn],dp[maxn][maxn];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {n=read();for(int i=1;i<=n;++i) w[i]=read();for(int i=n;i;--i) w[i]+=w[i+1];for(int i=n;i;--i) for(int j=1;j<=n-i+1;++j) dp[i][j]=max(dp[i][j-1],w[i]-dp[i+j][min(n-i-j+1,2*j)]);printf("%d",dp[1][2]);return 0; }

?

牛的詞匯The Cow Lexicon:

水題一道,先把每一段可以匹配的最大值預(yù)處理出來,然后dp,注意細(xì)節(jié)問題。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=600+10; int n,m,len[maxn],mch[maxn][maxn],dp[maxn],debug; char s[maxn],c[maxn][30];int main() {scanf("%d%d",&n,&m);scanf("%s",s+1); for(int i=1;i<=n;++i) {scanf("%s",c[i]+1);len[i]=strlen(c[i]+1);}int x,y;for(int i=1;i<=m;++i) {for(int j=1;j<=n;++j) {if(len[j]>m-i+1) continue;x=i;y=1;while(y<=len[j]&&x<=m) {while(x<=m&&s[x]!=c[j][y]) x++;if(x<=m&&s[x]==c[j][y]) y++,x++;}if(y>len[j]) mch[i][x-1]=max(mch[i][x-1],len[j]);}}for(int l=1;l<m;++l) for(int i=1;i<=m-l;++i) {mch[i][i+l]=max(mch[i][i+l],mch[i][i+l-1]);mch[i][i+l]=max(mch[i][i+l],mch[i+1][i+l]);}for(int i=1;i<=m;++i) {dp[i]=max(dp[i],dp[i-1]);for(int j=0;j<i;++j) dp[i]=max(dp[i],dp[j]+mch[j+1][i]);}printf("%d",m-dp[m]);return 0; }

  

化工廠裝箱員:

這個(gè)題很有趣啊,感覺題目描述簡(jiǎn)直有毒,看懂題了之后dp比較裸,不要管我惡意壓縮代碼。使手中保持10個(gè)成品讓dp非常簡(jiǎn)單暴力。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=100+10,maxt=13,maxs=13*13*13; int n,dp[maxn][maxs],sum[maxn][4],mi[5]; char cc;void get_dp(int f,int i,int x,int y,int z) {int t=x+y*mi[1]+z*mi[2];dp[i][t]=min(dp[i][t],f+1); }int main() {scanf("%d",&n); int x,y,z,now;for(int i=1;i<=n;++i) {do cc=getchar();while(cc<'A'||cc>'C');x=cc-'A';for(int p=0;p<3;++p) sum[i][p]=sum[i-1][p];sum[i][x]++;}if(n<=10) {printf("%d",(sum[n][0]!=0)+(sum[n][1]!=0)+(sum[n][2]!=0));return 0;}mi[0]=1; for(int i=1;i<=3;++i) mi[i]=mi[i-1]*11;memset(dp,0x3f3f3f3f,sizeof(dp)); x=0;for(int i=0;i<3;++i) x+=mi[i]*sum[10][i];dp[10][x]=0;for(int i=10;i<=n;++i) for(int j=0;j<mi[3];++j) if(dp[i][j]<=n){x=j%mi[1]; y=j/mi[1]%mi[1]; z=j/mi[2];if(i==n) {now=(x!=0)+(y!=0)+(z!=0);dp[n][0]=min(dp[n][0],dp[n][j]+now);continue;}if(x) now=min(n,i+x),get_dp(dp[i][j],now,sum[now][0]-sum[i][0],y+sum[now][1]-sum[i][1],z+sum[now][2]-sum[i][2]);if(y) now=min(n,i+y),get_dp(dp[i][j],now,x+sum[now][0]-sum[i][0],sum[now][1]-sum[i][1],z+sum[now][2]-sum[i][2]);if(z) now=min(n,i+z),get_dp(dp[i][j],now,x+sum[now][0]-sum[i][0],y+sum[now][1]-sum[i][1],sum[now][2]-sum[i][2]);}printf("%d",dp[n][0]);return 0; }

  

TA-Station:

又是一個(gè)裸的樹dp。不知道大家是怎么選難度等級(jí)的。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long const int maxn=1e6+10; int n; ll ans,nowans=1;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int fir[maxn],to[2*maxn],nxt[2*maxn],e=0; void add(int x,int y) {to[++e]=y;nxt[e]=fir[x];fir[x]=e;to[++e]=x;nxt[e]=fir[y];fir[y]=e; }ll dep[maxn],size[maxn],fa[maxn]; void dfs(int pos,int d) {dep[pos]=d; size[pos]=1; ans+=d; int y,z;for(y=fir[pos];y;y=nxt[y]) {if((z=to[y])==fa[pos]) continue;fa[z]=pos; dfs(z,d+1); size[pos]+=size[z];} }void dfs2(int pos,ll now,ll tot) {int y,z;if(now>ans||(now==ans&&nowans>pos)) ans=now,nowans=pos;for(y=fir[pos];y;y=nxt[y]) {if((z=to[y])==fa[pos]) continue;dfs2(z,now+tot+size[pos]-2*size[z],tot+size[pos]-size[z]);} }int main() {n=read(); int x,y;for(int i=1;i<n;++i) {x=read(); y=read();add(x,y);}dfs(1,1); dfs2(1,ans,0);printf("%lld",nowans);return 0; }

  

打鼴鼠:

還是不懂這道題哪里算得上提高+/省選-難度了。

首先我們不可能直接在n*n的網(wǎng)格圖上跑,只能用鼴鼠來dp。

然后考了一個(gè)小優(yōu)化就是當(dāng)兩個(gè)鼴鼠的time差>=2*n-2的時(shí)候無(wú)論怎樣都是可以轉(zhuǎn)移的。把鼴鼠按照time排序后就直接維護(hù)一個(gè)之前的鼴鼠與現(xiàn)在鼴鼠time差>=2*n-2的所有鼴鼠的dp最大值。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=1e4+10; int l,n,dp[maxn],time[maxn],x[maxn],y[maxn],ans,now;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {l=read(); n=read(); int pos=1;for(int i=1;i<=n;++i) {time[i]=read();x[i]=read(); y[i]=read();while(pos<i&&time[i]-time[pos]>=2*n-2) now=max(now,dp[pos++]); dp[i]=max(dp[i],now);for(int j=i-1;j>=pos;--j) if(abs(x[i]-x[j])+abs(y[i]-y[j])<=time[i]-time[j]) dp[i]=max(dp[i],dp[j]);dp[i]++;ans=max(ans,dp[i]);}printf("%d",ans);return 0; }

  

百日旅行:

又不小心開了一道水題,可以直接貪心水過。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long const int maxn=2e5+10; const ll INF=1e17; ll n,p,q,ans=INF;ll trav(ll day,ll times) {ll x=day/times,y=day%times;return p*(times-y)*x*x+p*y*(x+1)*(x+1); }int main() {scanf("%lld%lld%lld",&n,&p,&q);for(int i=0;i<=n;++i) ans=min(ans,i*q+trav(n-i,i+1));printf("%lld",ans);return 0; }

  

跳舞:

我覺得我不能再刷水題了。。。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=5000+10,INF=1e8; int n,T,s[maxn],b[maxn],dp[maxn][maxn],ans;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {n=read(); T=read();for(int i=1;i<=n;++i) s[i]=read();for(int i=1;i<=n;++i) b[i]=read();for(int i=0;i<=n;++i) for(int j=0;j<T;++j) dp[i][j]=-INF;dp[0][0]=0; int x;for(int i=0;i<n;++i) for(int j=0;j<T;++j)if(dp[i][j]>-INF) {x=(j+1)%T;dp[i+1][j]=max(dp[i+1][j],dp[i][j]-s[i+1]);if(x) dp[i+1][x]=max(dp[i+1][x],dp[i][j]+s[i+1]);else dp[i+1][x]=max(dp[i+1][x],dp[i][j]+s[i+1]+b[i+1]);}for(int i=0;i<T;++i) ans=max(ans,dp[n][i]);printf("%d",ans);return 0; }

  

豪華游輪:

貪心,考慮把向前走的和向后走的分別合并,然后讓中間角度盡可能接近180,剩下角度最后轉(zhuǎn)。

忘記了什么是余弦定理,忘記了角度和弧度怎么轉(zhuǎn)換,特別難過。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define db double const int maxn=50,maxh=1e6+10; const db pi=acos(-1); int n,t; db dis1,dis2,rnd[maxn],ans; char s[22]; bool vis[400],g[400];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {n=read();for(int i=1;i<=n;++i) {scanf("%s",s);if(s[0]=='r') rnd[++t]=read()%360;else if(s[0]=='l') rnd[++t]=(720-read())%360;else if(s[0]=='f') dis1+=read();else if(s[0]=='b') dis2+=read();}vis[0]=1;for(int i=1;i<=t;++i) {memcpy(g,vis,sizeof(g));for(int j=0;j<360;++j) if(g[j]) vis[(int)(j+rnd[i])%360]=1;}for(int i=0;i<=180;++i) if(vis[180+i]||vis[180-i]) {ans=sqrt(dis1*dis1+dis2*dis2-2*dis1*dis2*cos((double)(180-i)*pi/180));break;}printf("%.6lf",ans);return 0; }

  

前綴單詞:

我說我在后面開幾道不是水題的題做,然后就看到這道通過三十多的題,我覺得應(yīng)該是那種有點(diǎn)難度但是不至于太難的題,但是沒想到還是水題,樹dp水題,直接建字典樹。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll unsigned long long const int maxn=50+5,maxs=maxn*maxn; int n,son[maxs][30],p[maxs],t; char s[maxn]; ll dp[maxn];void add_s(int pos) {int len=strlen(s+1),now=0,x;for(int i=1;i<=len;++i) {x=s[i]-'a';if(!son[now][x]) son[now][x]=++t;now=son[now][x];}p[now]=pos; }int fir[maxs],to[maxs],nxt[maxs],e=0; void add(int x,int y) {to[++e]=y;nxt[e]=fir[x];fir[x]=e; }void dfs1(int pos,int now) {if(p[pos]) add(now,++t),now=t;for(int i=0;i<26;++i) if(son[pos][i]) dfs1(son[pos][i],now); }void dfs2(int pos) {dp[pos]=1; int y,z;for(y=fir[pos];y;y=nxt[y]) {dfs2(z=to[y]);dp[pos]*=dp[z];}dp[pos]++; }int main() {scanf("%d",&n);for(int i=1;i<=n;++i) scanf("%s",s+1),add_s(i);t=0;dfs1(0,0); dfs2(0);printf("%lld",dp[0]-1);return 0; }

  

起床困難綜合癥:

NOI還有水題的啊。。。。

直接算每一位如果是0最后會(huì)是什么,如果是1最后會(huì)是什么,然后直接數(shù)位dp就可以了。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; const int maxn=1e5+10,maxs=35; int n,m,now[2][maxs],dp[maxs],ans; char c[10];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }void get_now(int &f,int p,int x) {if(p==1) f=f&x;else if(p==2) f=f|x;else f=f^x; }int main() {n=read(); m=read(); int x,y;for(int i=0;i<=32;++i) now[0][i]=0,now[1][i]=1;for(int i=1;i<=n;++i) {scanf("%s",c);y=read();x= c[0]=='A'? 1 : (c[0]=='O'? 2:3);for(int j=0;j<=32;++j) {get_now(now[0][j],x,y&1);get_now(now[1][j],x,y&1);y>>=1;}}for(int i=0;i<=32;++i) {dp[i]=dp[i-1];dp[i]|=max(now[0][i],now[1][i])<<i;}x=0;for(int i=32;~i;--i) {if((y=((m>>i)&1))) ans=max(ans,(x|((now[0][i])<<i))|dp[i-1]);x|=((now[y][i])<<i);}ans=max(ans,x);printf("%d",ans);return 0; }

  

子串:

不是很難,用f[i][j][t][r]表示當(dāng)前我們A串匹配到了i位置,B串匹配到了j位置,并且一共取了t個(gè)子串,目前是否為最后取的子串的最后一位的狀態(tài)。

第一維滾動(dòng),直接dp。注意細(xì)節(jié)問題。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll long long const int maxn=1000+10,maxm=200+10; const ll mod=1e9+7; int n,m,k; char a[maxn],b[maxn]; ll f[2][maxm][maxm][2];int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int main() {n=read();m=read();k=read();scanf("%s%s",a+1,b+1);int cur=0; f[0][0][1][1]=1;for(int i=1;i<=n;++i) {cur^=1;memset(f[cur],0,sizeof(f[cur]));for(int j=1;j<=m;++j) for(int t=1;t<=min(j,k);++t) {if(a[i]==b[j]) (f[cur][j][t][1]+=f[cur^1][j-1][t][1]+f[cur^1][j-1][t-1][0]+f[cur^1][j-1][t-1][1])%=mod;(f[cur][j][t][0]+=f[cur^1][j][t][0]+f[cur^1][j][t][1])%=mod;}f[cur][0][1][1]=1;}printf("%lld",(f[cur][m][k][0]+f[cur][m][k][1])%mod);return 0; }

  

樹:

這道題說簡(jiǎn)單吧,但是要寫高精,說難吧,其實(shí)dp方程和轉(zhuǎn)移也沒多難。

用 $ dp[pos][i] $ 表示以 $ pos $ 為根的子樹中, $ pos $ 所在連通塊大小為i的最大答案(不乘i)。

然后$ dp[pos][0]= \max ( dp[pos][i] \times i ) $ 。

kczno1題解中說復(fù)雜度看起來像三方的實(shí)際上是平方的:

每次轉(zhuǎn)移是復(fù)雜度是 x之前的子樹的sz*當(dāng)前子樹的sz

相當(dāng)于之前子樹所有點(diǎn)和當(dāng)前子樹的點(diǎn)組成的點(diǎn)對(duì)數(shù)

而每個(gè)點(diǎn)對(duì)只會(huì)在lca處被計(jì)算一次

所以復(fù)雜度O(n^2)

這樣轉(zhuǎn)移好處是可以沒有除法。

我一開始沒寫高精交上去發(fā)現(xiàn)WA了一堆,我把long long 改成 unsigned long long 發(fā)現(xiàn)答案變了。

然后就檢查轉(zhuǎn)移方程檢查了很久,最后棄療去看題解,發(fā)現(xiàn)竟然和題解的思路一模一樣,除了他寫了高精。

不想打高精了,放個(gè)半成品在這。

#include<algorithm> #include<iostream> #include<cstring> #include<cstdlib> #include<cstdio> #include<cmath> using namespace std; #define ll unsigned long long const int maxn=700+10; int n;int aa;char cc; int read() {aa=0;cc=getchar();while(cc<'0'||cc>'9') cc=getchar();while(cc>='0'&&cc<='9') aa=aa*10+cc-'0',cc=getchar();return aa; }int fir[maxn],to[2*maxn],nxt[2*maxn],e=0; void add(int x,int y) {to[++e]=y;nxt[e]=fir[x];fir[x]=e;to[++e]=x;nxt[e]=fir[y];fir[y]=e; }ll size[maxn],dp[maxn][maxn]; void dfs(int pos,int f) {int y,z;size[pos]=1;for(y=fir[pos];y;y=nxt[y]) {if((z=to[y])==f) continue;dfs(z,pos);size[pos]+=size[z];}dp[pos][1]=1; ll t;for(y=fir[pos];y;y=nxt[y]) {if((z=to[y])==f) continue;for(int i=size[pos];i;--i) {t=dp[pos][i]*dp[z][0];for(int j=min(i-1,(int)size[z]);j;--j)dp[pos][i]=max(dp[pos][i],dp[pos][i-j]*dp[z][j]);dp[pos][i]=max(dp[pos][i],t);}}for(int i=1;i<=size[pos];++i) dp[pos][0]=max(dp[pos][0],dp[pos][i]*i); }int main() {n=read(); int x,y;for(int i=1;i<n;++i) {x=read(); y=read();add(x,y);}dfs(1,0);cout<<dp[1][0];return 0; }

  

轉(zhuǎn)載于:https://www.cnblogs.com/Serene-shixinyi/p/7755456.html

總結(jié)

以上是生活随笔為你收集整理的动态规划复习的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 一二三区中文字幕 | 国产三级小视频 | 日本中文字幕在线观看视频 | 波多野结衣不卡视频 | 在线观看亚洲色图 | 日本涩涩网站 | 久久久久久电影 | 久久精品国产亚洲av麻豆图片 | 欧美少妇精品 | 成人三级图片 | www.黄色片| 欧美成免费 | 男人天堂aaa | 牲欲强的熟妇农村老妇女视频 | 欧美精品久久久久性色 | 亚日韩av| 久久免费国产精品 | 免费搞黄网站 | 国产日韩精品在线观看 | 懂色av一区二区三区在线播放 | 爱爱爱爱网站 | 国产新婚疯狂做爰视频 | 欧美成人免费在线观看视频 | 哈利波特3在线观看免费版英文版 | 中国黄色片子 | 国产精品一亚洲av日韩av欧 | 中国黄色一级毛片 | 一区二区免费看 | 99精品网站 | 欧美淫 | 91最新地址永久入口 | 一本视频 | 狠狠干夜夜操 | 久久综合久色欧美综合狠狠 | 寂寞少妇让水电工爽hd | 精品人妻一区二区免费视频 | 国产视频一二三区 | 亚洲视频在线观看一区二区 | 国产欧美一区二区三区在线老狼 | 四虎影院www | 色吊丝网站 | 日本熟妇毛茸茸丰满 | 超碰97在线资源 | 成人小说亚洲一区二区三区 | 色欲人妻综合网 | 日韩欧美精品一区二区三区 | 精品国产综合区久久久久久 | 1000部啪啪未满十八勿入 | 日韩天天操 | 午夜免费福利网站 | 日本wwwwww | 中文有码在线播放 | 男插女在线观看 | 一区不卡在线 | 国产chinesehd精品露脸 | 色婷婷视频 | 久久这里有精品视频 | 色狠狠一区二区三区香蕉 | 欧美日韩国产专区 | 亚洲视频网站在线观看 | 性生交大片免费看 | 视色影视 | 亚洲第一视频区 | www黄色网 | 色成人亚洲 | 天堂av一区二区三区 | 免费看一级 | 日本少妇吞精囗交视频 | 91成人免费看片 | 国产精品腿扒开做爽爽爽挤奶网站 | 91喷水视频 | 国产一级在线播放 | 男男gay动漫| 日日操天天操夜夜操 | 久久重口味 | 成人中文在线 | 一区二区三区波多野结衣 | 久久综合成人 | 国产99久久久国产精品成人免费 | 91视频入口 | 欧美黄色三级视频 | 一区二区三区人妻 | 日韩av麻豆 | 伊人网影院 | 91精品国产综合久久久蜜臀 | 亚洲欧洲视频在线观看 | 国产香蕉在线观看 | av夜夜| 三级黄色av | 亚洲精品乱码久久久久99 | 日韩精品欧美激情 | av日韩在线播放 | 日韩黄色一级片 | www日日| 四虎综合网 | 午夜生活片 | 2018天天操 | 神马久久久久久久久久久 | 超碰在线天天 |