好吧,這名字真是讓我想起了某段被某教科書支配的歷史.....各種DP題層出不窮,不過終於做完了orz
雖然各種手糊加亂搞,但還是要總結一下.
T1 Monkey Banana Problem
這道題和數塔問題差不多,不過多了一個倒過來的
代碼如下:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; long long a[201][101],t,n; int main() { int ttt=0; scanf("%lld",&t); while(t--) { ttt++; memset(a,0,sizeof(a)); scanf("%lld",&n); for(int i=1; i<=n; i++) { for(int j=1; j<=i; j++) { scanf("%lld",&a[i][j]); } } for(int i=n+1; i<=2*n-1; i++) { for(int j=1; j<=2*n-i; j++) { scanf("%lld",&a[i][j]); } } for(int i=2; i<=n; i++) { for (int j=1; j<=i; j++) { a[i][j]+=max(a[i-1][j-1],a[i-1][j]); } } for(int i=n+1; i<=2*n-1; i++) { for(int j=1; j<=2*n-i; j++) { a[i][j]+=max(a[i-1][j],a[i-1][j+1]); } } printf("Case %d: %lld\n",ttt,a[2*n-1][1]); } }
T2Rooks
T3Marriage Ceremonies
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 17 int dp[N][1<<N]; int a[N][N]; int t,n,ans; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); scanf("%d",&n); for(int i=0; i<n; i++) { for(int j=0; j<n; j++) { scanf("%d",&a[i][j]); } } for(int i=0; i<n; i++) { dp[0][(1<<i)]=a[0][i]; } for(int i=1; i<n; i++) { for(int sta=0; sta<=(1<<n)-1; sta++) { if(!dp[i-1][sta]) { continue; } for(int j=0; j<n; j++) { if(sta&(1<<j)) { continue; } dp[i][sta|(1<<j)]=max(dp[i][sta|(1<<j)],dp[i-1][sta]+a[i][j]); } } } printf("Case %d: %d\n",ttt,dp[n-1][(1<<n)-1]); } }
T4Guilty Prince
代碼:
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; char maze[30][30]; bool vis[30][30]; int t,n,m,ans,x,y; int bfs(int x,int y) { queue<pair<int,int> > q; q.push(make_pair(x,y)); vis[x][y]=1; while(!q.empty()) { int nx=q.front().first; int ny=q.front().second; q.pop(); if(!vis[nx+1][ny]&&nx+1<=n&&nx+1>=1&&ny<=m&&ny>=1) { q.push(make_pair(nx+1,ny)); ans++; vis[nx+1][ny]=1; } if(!vis[nx-1][ny]&&nx-1<=n&&nx-1>=1&&ny<=m&&ny>=1) { q.push(make_pair(nx-1,ny)); ans++; vis[nx-1][ny]=1; } if(!vis[nx][ny+1]&&nx<=n&&nx>=1&&ny+1<=m&&ny+1>=1) { q.push(make_pair(nx,ny+1)); ans++; vis[nx][ny+1]=1; } if(!vis[nx][ny-1]&&nx<=n&&nx>=1&&ny-1<=m&&ny-1>=1) { q.push(make_pair(nx,ny-1)); ans++; vis[nx][ny-1]=1; } } } int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; ans=1; scanf("%d%d",&m,&n); memset(vis,0,sizeof(vis)); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { cin>>maze[i][j]; if(maze[i][j]=='@') { x=i; y=j; } if(maze[i][j]=='#') { vis[i][j]=1; } } } bfs(x,y); printf("Case %d: %d\n",ttt,ans); } }
T5 Brush (III)
給你一個刷子,刷子有寬度w,允許你平行x軸刷k次,告訴你有n個點,求刷k次能刷完的最多的點的個數.
好吧,最初就是想暴力亂搞,然后發現xi,yi都有10^9,就想到了離散化.離散化y的坐標,統計一個cnt[i]表示第i個y值上有幾個點,然后再搞一個num[i]表示以y[i]為下界寬為w的區域中點的個數,不過因為cnt數組足以勝任這一點。
然后就可以開始dp了
對於每一個cnt[i],我們可以選擇從這個點開始刷還是不刷,對於每個點來說往左刷一個單位還是往右刷一個單位顯然是沒有任何意義的,即我們還是得從以每個點開始dp
很容易推出dp的轉移方程
dp[i][j]表示刷到第j個點以后已經刷了i次的最大值
那么dp[i][j]=max{dp[i][j],dp[i-1][( 1~( j-1))]+cnt[j]}
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 110 struct point { int x,y; } poi[N]; int n,w,k,t,tmp,ans; int cnt[N],y[N],dp[N][N]; bool cmp(point a,point b) { return a.y<b.y; } void discretization_it() { sort(poi+1,poi+n+1,cmp); tmp=1; y[tmp]=poi[1].y; cnt[tmp]=1; for(int i=2; i<=n; i++) { if(poi[i].y==y[tmp]) { cnt[tmp]++; } else { y[++tmp]=poi[i].y; cnt[tmp]=1; } } for(int i=1; i<=tmp; i++) { for(int j=i+1; j<=tmp; j++) { if(y[j]-y[i]<=w) { cnt[i]+=cnt[j]; } } } } int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; ans=0; memset(dp,0,sizeof(dp)); scanf("%d%d%d",&n,&w,&k); for(int i=1; i<=n; i++) { scanf("%d %d",&poi[i].x,&poi[i].y); } discretization_it(); for(int i=1; i<=tmp; i++) { dp[1][i]=cnt[i]; } for(int i=2; i<=k;i++) { for(int j=1; j<=tmp; j++) { for(int l=1; l<j; l++) { if (y[j]-y[l]<=w) { break; } if (y[j]-y[l]>w&&dp[i][j]<dp[i-1][l]+cnt[j]) { dp[i][j]=dp[i-1][l]+cnt[j]; } } } } for(int i=1;i<=k;i++) { for(int j=1;j<=tmp;j++) { ans=max(ans,dp[i][j]); } } printf("Case %d: %d\n",ttt,ans); } }
T6 Brush (IV)
這回的刷子升級了,可以向任意的方向刷,不過刷子沒有寬度,即幾個點共線才能被刷掉,求刷掉所有點的最小次數
因為點的個數的數據范圍只有16,所以可以想到狀態壓縮動態規划,選取i,j兩個點,無非是選擇是否要連i,j然后如果要選的話就把和i,j三點共線的所有點都加到當前的狀態里去,然后花費1的次數將之前狀態轉移到現在狀態。假設a[i][j]為i,j所在直線上的點的個數,這里a[i][j]表示二進制位下在i,j直線上所有點的位置。
所以dp[sta|a[i][j]]=min{dp[sta|a[i][j],dp[sta]+1}
代碼:
#include<cstdio> #include<vector> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define inf 0x3f3f3f3f struct point { int x,y; }poi[20]; int a[20][20]; int dp[(1<<17)]; int n,t; vector <int> yyk[(1<<17)]; //int lowbit(int x) //{ // return x&-x; //} bool judge(point x,point y,point z) { int tmp=(y.x-x.x)*(z.y-x.y)-(y.y-x.y)*(z.x-x.x); if(!tmp) { return 1; } else { return 0; } } void pre() { memset(a,0,sizeof(a)); for(int i=0;i<n;i++) { a[i][i]|=(1<<i); for(int j=i+1;j<n;j++) { for(int k=0;k<n;k++) { if(judge(poi[i],poi[j],poi[k])) { a[i][j]|=(1<<k); } } } } } void db() { for(int sta=0;sta<=(1<<16);sta++) { for(int j=0;j<16;j++) { if((sta&1<<j)==0) { yyk[sta].push_back(j); } } } } int main() { int ttt=0; scanf("%d",&t); db(); while(t--) { ttt++; scanf("%d",&n); for(int i=0;i<=(1<<n);i++) { dp[i]=inf; } for(int i=0;i<n;i++) { scanf("%d %d",&poi[i].x,&poi[i].y); } pre(); dp[0]=0; for(int sta=0;sta<(1<<n)-1;sta++) { int x=yyk[sta][0]; for(int j=0;j<yyk[sta].size();j++) { int y=yyk[sta][j]; dp[sta|a[x][y]]=min(dp[sta|a[x][y]],dp[sta]+1); } } printf("Case %d: %d\n",ttt,dp[(1<<n)-1]); } return 0; }
T7 Painful Bases
給出一個值base,一個值k,一個base進制下的的數,求該數的全排列中模k余0的數的個數。
emmmm。。。這倒是比較老的套路了。。。首先看到求模數為幾,自然是想到開一維來記錄模數,這有點類似於數位dp中某些題的思路。而再乍一看這個給的數的長度不會超過16,自然是想到用狀壓去做。所以設置dp[sta][mod]表示當已經放好sta個數后這些數余數為mod的數的個數。
所以狀態轉移方程如下:dp[sta|(1<<j)][(mod*base+a[j]%k)%k]+=dp[sta][mod]
代碼:
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; long long dp[30][(1<<17)]; char s[20]; int t,base,k,a[20]; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; memset(dp,0,sizeof(dp)); scanf("%d%d",&base,&k); cin>>s; int len=strlen(s); for(int i=0;i<len;i++) { if(s[i]<='9'&&s[i]>='0') { a[i]=s[i]-'0'; } else { a[i]=s[i]-'A'+10; } } int up=1<<len; dp[0][0]=1; for(int sta=0;sta<up;sta++) { for(int j=0;j<len;j++) { if(sta&(1<<j)) { continue; } for(int x=0;x<k;x++) { if(!dp[x][sta]) { continue; } dp[(x*base+a[j])%k][sta|(1<<j)]+=dp[x][sta]; } } } printf("Case %d: %lld\n",ttt,dp[0][up-1]); } }
T8 the specials menu
給出一個字符串,求這個字符串中回文串的個數,你可以去掉一些字母。
噫,又是套路。。。區間dp老題了
用dp[i][j]表示區間i-j之間的方案數
則存在以下兩種轉移
1.s[i]=s[j]時:只留ij顯然多了一種方案,dp[i][j]=dp[i+1][j]+dp[i][j-1]+1
2.s[i]!=s[j]時:顯然存在重復dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]
代碼:
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 110 long long dp[N][N],t,ttt; int main() { scanf("%lld",&t); while(t--) { char s[N]; ttt++; memset(dp,0,sizeof(dp)); cin>>s+1; int len=strlen(s+1); for(int i=1;i<=len;i++) { dp[i][i]=1; } for(int i=len;i>=1;i--) { for(int j=i+1;j<=len;j++) { dp[i][j]+=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]; if(s[i]==s[j]) { dp[i][j]+=dp[i+1][j-1]+1; } } } printf("Case %lld: %lld\n",ttt,dp[1][len]); } }
T9 A Dangerous Maze
有n扇門,每扇門對應着一個值(xi),為正代表xi分后這個人能夠離開,為負表示abs(xi)分后這個人會重新回到原地,並且這個人仍是1/n的概率重新再選一扇門。求他離開的期望?
好吧。。。感覺這道題又和dp沒關系啊。。。
這是一道純種的期望題。
首先對於門只有兩種功能:要么把人傳回去,要么送他出來,假設期望為E。當前耗時為C
這扇門是好門,將你傳送出去,期望為C*1/n
這扇門是壞門,將你傳送回來,期望為(E+C)*1/n
設總共有X扇壞門,好門消耗時間總和為sum1,壞門為sum2
則可以列出方程:
E=(sum1)/n+(E*X+sum2)/n
E=(sum1+sum2+E*X)/n
nE=sum1+sum2+E*X
(n-X)*E=sum1+sum2;
E=(sum1+sum2)/(n-X)
當然n-X為零的話他就永遠出不去了
然而與dp並無關系的說。。。
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int t,n,a,sum,div1,nag,pos; int gcd(int a,int b) { if(a%b==0) { return b; } return gcd(b,a%b); } int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; nag=0; sum=0; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a); if(a<0) { nag++; sum-=a; } else { sum+=a; } } pos=n-nag; if(pos==0) { printf("Case %d: inf\n",ttt); } else { printf("Case %d: %d/%d\n",ttt,sum/gcd(pos,sum),pos/gcd(pos,sum)); } } return 0; }
T10 Discovering Gold
已知有一個寬為1,長為n的表格,表格由1*1的小正方形組成,每個小正方形中都有一個價值v[i],現在從起點1開始擲骰子,擲到幾就向前走幾步,並獲得當前走到位置的值v,
如果投過n就重投。求獲得價值的期望。
著名oi選手xxx曾說過“一切期望皆方程”
好的這又不是一道dp題(大霧)
總而言之,著名oi選手xxx說過“概率正推,期望倒推”
所以DP[I]=(DP[I+1]+DP[I+2]+DP[I+3]+DP[I+4]+DP[I+5]+E[DP+6])/6+V[I]
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 110 int a[N]; int n,t; double dp[N]; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; scanf("%d",&n); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } dp[n]=a[n]; for(int i=n-1;i>=1;i--) { int up; dp[i]=a[i]; if(n-i<6) { up=n-i; } else { up=6; } for(int j=1;j<=up;j++) { dp[i]+=(1.0/up)*dp[i+j]; } } printf("Case %d: %.6lf\n",ttt,dp[1]); } }
T11 Easy Game
有一個子序列,有兩個人輪流取,每次可以從左到右或從右到左取任意長度的數,使先取的與后取的差最大,並輸出這個差。
一開始感覺是道博弈題啊。。。然后立馬想到好像傳說有博弈dp這種操作,但我太菜了不會啊!
不管怎么樣,我們要對出題人有足夠的信仰。
先想想策略吧,當前情況下無非是從左去或從右取兩種操作,但如果只是取能取的最大值必然有后效性,諸如樣例1
假設現在只有一個點,那么因為先手所以必須要拿,
如果有兩個呢?考慮要么兩個都拿,要么拿左邊一個,要么拿右邊一個。
如果有三個呢?要么全拿,要么拿左邊兩個,要么拿一個,而如果拿了一個,后者能取得的最優解就是去掉這一個以后拿兩個的方案。顯然右邊也是如法炮制。
如果四個呢?對於先拿一個、兩個的情況,后手的最優策略同樣如上。
然后瞬間明了,這原來是道區間dp。。。
用dp[i][j]來表示i-j的區間中的最大差。
顯然dp[i][i]就是a[i],然后假設sum[i][j]存的是i-j的前綴和(這只是為了方便理解,事實一維即可)
結合上面的推導
dp[i][j]=max{sum[i][j],sum[i][k]-dp[k+1][j],sum[k+1][j]-dp[i][k]}(i<=k<j)
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 110 #define INF 0x3f3f3f3f int dp[N][N],a[N],sum[N]; int n,t; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; memset(dp,0,sizeof(dp)); memset(a,0,sizeof(a)); scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); sum[i]=sum[i-1]+a[i]; } for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { dp[i][j]=-INF; } dp[i][i]=a[i]; } for(int k=2;k<=n;k++) { for(int i=1;i+k-1<=n;i++) { int j=i+k-1; dp[i][j]=sum[j]-sum[i-1]; for(int mid=i;mid<j;mid++) { dp[i][j]=max(dp[i][j],sum[mid]-sum[i-1]-dp[mid+1][j]); } for(int mid=j;mid>i;mid--) { dp[i][j]=max(dp[i][j],sum[j]-sum[mid-1]-dp[i][mid-1]); } } } printf("Case %d: %d\n",ttt,dp[1][n]); } return 0; }
T12 Fast Bit Calculation
一個數字在二進制下,如果一個1的右邊還是1,則權值+1
諸如 11011 權值為2,11111權值為4
然后給你一個數x,求1-x之間所有數的權值之和
顯然是數位dp,然而yyk大佬告訴我,我實在是太naive了,這道題可以用遞推做!
想來也是,其實數位dp究其根源也只是一種暴力。
我們假設sum[i]表示1-i的權值和
那么
Sum[0]=0
Sum[1]=0
Sum[3]=1
Sum[7]=4
這幾個數據還是可以手模出來的
然后對於下面該怎么辦呢?
比如要算sum[15]
那么15比7要多了一個二進制位,所以說對於該位為0或1都存在sum[7]
所以sum[15]中首先包含兩個sum[7]
而如果第四位和第三位都為1則會再多做一個貢獻。而這些個數總共有2^(i-2)個,也就是前兩位都確定了,后面幾位的所有排列數
所以
Sum[15]=2*sum[7]+2^(i-2)
當然這個公式是針對每個2^i-1才有用的。
那普通的該怎么算呢?
顯然如果對於一個二進制形式的數首位為1說明此位為0的情況是全被包含的
ans+=sum[i<<len]
然后刪去這一位繼續處理剩下的位數,就ok了!
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; long long a[35]; int n,t; void db() { a[0]=0; a[1]=0; a[2]=1; a[3]=4; for(int i=4; i<=31; i++) { a[i]=(1<<(i-2))+(a[i-1]<<1); } } long long get() { int temp=n; long long ans=0,x=1; for(int i=31; i>=1; i--) { if(temp&(x<<i)) { n^=(x<<i); ans+=a[i]+max((long long)0,n-(x<<(i-1))+1); } } return ans; } int main() { int ttt=0; db(); scanf("%d",&t); while(t--) { ttt++; scanf("%d",&n); printf("Case %d: %lld\n",ttt,get()); } return 0; }
T13 Generating Palindromes
給你一個字符串,可以在任意位置插任意個字母,求至少插幾個字母后會產生回文串?
這道題又是套路。。。
不過由於T8比較敷衍,還是好好講講吧。。。(然后講錯了)
對於每1個字母,都是回文串,並不需要任何操作。
2個字母,如果相等自然是0,不等是1
3個字母,如果左右相等,就是1個字母的方案。如果不相等,就是左邊取2個的方案加補第三個或者右邊取兩個加補第一個。
4個字母,如果左右相等,就是第2個字母的方案。如果不相等,就是取左邊3個加補第4個或、取右邊3個加補第1個
綜上所述
狀態轉移方程就可以推出來了
設dp[i][j]表示i-j之間全部處理成回文串的花費。
則
(s[i]==s[j])
Dp[i][j]=dp[i+1][j-1]
(s[i]!=s[j])
Dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1
代碼:
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; #define N 110 int dp[N][N],t,ttt; int main() { scanf("%d",&t); while(t--) { char s[N]; ttt++; memset(dp,0,sizeof(dp)); cin>>s+1; int len=strlen(s+1); for(int i=len;i>=1;i--) { for(int j=i+1;j<=len;j++) { dp[i][j]=min(dp[i+1][j],dp[i][j-1])+1; if(s[i]==s[j]) { dp[i][j]=min(dp[i][j],dp[i+1][j-1]); } } } printf("Case %d: %d\n",ttt,dp[1][len]); } }
T14 A Refining Company
在新日暮里中生活着一群快樂的比利和VAN様,但由於他們天天摔跤,產生的DARK值已經快把新日暮里毀掉了!作為新日暮里的管理者,你需要把比利送向西邊,VAN様送向北邊,
已知新日暮里是一個N*M的矩形,每個1*1的格子里都有一定數量的比利和VAN様,現讓你安排一些大巴車,這些大巴車的起點可以在任意位置,但方向只能向西或向北,不能拐彎。
如果向北,沿途的所有比利可以上車。如果向西,沿途的所有比利可以上車。為了防止他們越車摔跤,任意兩個公交車的路線不能相交,求能安置好的比利和VAN様的最大數量
這是一道水題( ・´ω`・ ),思路非常簡單。
幾乎是一眼出方程系列:
Dp[i][j]=max(dp[i-1][j]+sum_line[i][j],dp[i][j-1]+sum_row[i][j])
代碼:
#include<cstdio> #include<string> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,m,t; int a,b,suma[505][505],sumb[505][505]; long long dp[505][505]; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; memset(suma,0,sizeof(suma)); memset(sumb,0,sizeof(sumb)); memset(dp,0,sizeof(dp)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%d",&a); suma[i][j]=suma[i][j-1]+a; } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { scanf("%d",&b); sumb[i][j]=sumb[i-1][j]+b; } } for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { dp[i][j]=max(dp[i][j-1]+sumb[i][j],dp[i-1][j]+suma[i][j]); } } printf("Case %d: %d\n",ttt,dp[n][m]); } }
T15 Agent 47
開局只有一個人一把槍,裝備全靠打。
告訴你n個小怪的血量以及他們的裝備能對其他小怪的傷害,你一開始的武器只能對任何一個小怪造成1點傷害,打死一個小怪后能獲得他的裝備,並用來打其他的小怪。求消滅所有小怪的最少槍數。
因為n很小,只有15,所以理所應當地想到了狀態壓縮動態規划
假設一個狀態sta中1表示已經被打死的小怪,0表示還沒有打死
每次轉移顯然是從當前狀態中挑出一個打死,花費是小怪血量除以當前所有武器中的最大值
然后更新加上這個被打死的小怪的狀態
轉移方程:設t為打死小i的最小次數
Dp[sta|(1<<i)]=min(dp[sta|(1<<i)],dp[sta]+t)
代碼:
#include<cstdio> #include<cstring> #include<algorithm> #define N (1<<15)+10 #define INF 0x3f3f3f3f using namespace std; int dp[N],hp[22],map[22][22],n; char line[20]; int main() { int t,k=0; scanf("%d",&t); while(t--) { k++; memset(dp,INF,sizeof(dp)); scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&hp[i]); dp[1<<i]=hp[i]; } for(int i=0;i<n;i++) { scanf("%s",line); for(int j=0;j<n;j++) { map[i][j]=line[j]-'0'; } } int maxsta=(1<<n)-1; for(int sta=1;sta<=maxsta;sta++) { for(int j=0;j<n;j++) { if(sta&(1<<j)) { continue; } dp[sta|(1<<j)]=min(dp[sta]+hp[j],dp[sta|(1<<j)]); for(int x=0;x<n;x++) { if((sta&(1<<x))&&map[x][j]) { int tot=hp[j]/map[x][j]; if(hp[j]%map[x][j]) { ++tot; } dp[sta|(1<<j)]=min(dp[sta]+tot,dp[sta|(1<<j)]); } } } } printf("Case %d: %d\n",k,dp[maxsta]); } }
T16 Palindrome Partitioning
給你一個字符串,要求你划分使所有的子串都是回文串,求最小划分數
好吧並不想多解釋了,直接上代碼:
#include<cstdio> #include<algorithm> #include<cstring> #define INF 0x3f3f3f3f #define N 1010 using namespace std; char s[N]; int dp[N]; bool judge(int i,int j) { while(i<=j) { if(s[i]!=s[j]) { return 0; } i++; j--; } return 1; } int main() { int t,ttt=0; scanf("%d",&t); while(t--) { ttt++; scanf("%s", s); int len=strlen(s); for(int i=0;i<len;i++) { dp[i]=INF; for (int j=0;j<=i;j++) { if(judge(j,i)) { if(j == 0) { dp[i]=1; } else { dp[i]=min(dp[i],dp[j-1]+1); } } } } printf("Case %d: %d\n",ttt,dp[len-1]); } }
T17Neighbor House
這道題比T1還水。。。不說了,
上代碼:
#include<queue> #include<vector> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int dp[30][3],a[30][3],n,t,ans; int main() { int ttt=0; scanf("%d",&t); while(t--) { ttt++; ans=0; scanf("%d",&n); for(int i=1;i<=n;i++) { dp[i][0]=dp[i][1]=dp[i][2]=0x3f3f3f3f; } for(int i=1;i<=n;i++) { for(int j=0;j<3;j++) { scanf("%d",&a[i][j]); } } for(int i=1;i<=n;i++) { dp[i][0]=min(dp[i-1][1]+a[i][0],dp[i-1][2]+a[i][0]); dp[i][1]=min(dp[i-1][0]+a[i][1],dp[i-1][2]+a[i][1]); dp[i][2]=min(dp[i-1][0]+a[i][2],dp[i-1][1]+a[i][2]); } ans=min(dp[n][0],min(dp[n][1],dp[n][2])); printf("Case %d: %d\n",ttt,ans); } }
終於打完了。。。orz