分析:首先我们需要明白一个问题,就是每种电压的灯泡要么就是全部替换,要么全部不替换,为什么呢?因为如果只替换一半,那两种电源都需要,不划算,从另一个方面来说,既然转化一半会比原来小,那为什么不全部转换呢?接着根据题意我们应该把灯泡按照电压从小到大排序。然后我们令dp[i]表示1~i的最小开销,令sum[i]表示前i种灯泡的数量,则dp[i]=min(dp[j]+(sum[i]-sum[j])*p[i].c+p[i].k),表示前j个先用最优方案,然后j+1~i换成第i号电源。有人会问这样是否会漏解,比如第i个,只替换我们1,3,5这样子的,但是这种情况是不存在的,为什么呢?设i<j<k,如果i替换成k,而j没有替换,说明j是更优的,那i为什么不转移到j呢?
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 #include "algorithm" 6 using namespace std; 7 const int maxn=1000+10; 8 const int INF=1<<30; 9 struct Node{ 10 int v,k,c,l; 11 }; 12 Node p[maxn]; 13 int n; 14 bool cmp(Node a,Node b){ 15 return a.v<b.v; 16 } 17 int sum[maxn]; 18 int dp[maxn]; 19 int main() 20 { 21 while(cin>>n) 22 { 23 if(n==0) break; 24 for(int i=1;i<=n;i++){ 25 cin>>p[i].v>>p[i].k>>p[i].c>>p[i].l; 26 } 27 sort(p+1,p+1+n,cmp); 28 for(int i=1;i<=n;i++) 29 sum[i]=sum[i-1]+p[i].l; 30 dp[0]=0; 31 for(int i=1;i<=n;i++){ 32 dp[i]=INF; 33 for(int j=0;j<=i;j++){ 34 dp[i]=min(dp[i],dp[j]+(sum[i]-sum[j])*p[i].c+p[i].k); 35 } 36 } 37 cout<<dp[n]<<endl; 38 } 39 return 0; 40 }
分析:令dp[i]表示到第1~i个位置为止的最少回文串个数,所以这个位置必定是由上一个是以他开始的往前的回文串转换过来的,所以dp[i]=min(dp[j-1]+1),j为所有i之前且能够与i构成回文串的位置
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 #include "algorithm" 6 #include "vector" 7 using namespace std; 8 const int maxn=1000+10; 9 const int INF=1<<30; 10 char s[maxn]; 11 int T; 12 int dp[maxn]; 13 bool judge(int i,int j){ 14 int flag=0; 15 while(i<j){ 16 if(s[i]==s[j]){ 17 i++,j--; 18 }else{ 19 flag=1; break; 20 } 21 } 22 if(flag) return false; 23 return true; 24 } 25 int main() 26 { 27 cin>>T; 28 getchar(); 29 while(T--){ 30 memset(s,0,sizeof(s)); 31 char ch=getchar(); 32 int n=0; 33 while(ch!=' ') s[++n]=ch,ch=getchar(); 34 dp[1]=1; 35 for(int i=1;i<=n;i++){ 36 int num=-1; 37 vector<int> p; 38 for(int j=i;j>=1;j--){ 39 if(judge(j,i)){ 40 p.push_back(j); 41 } 42 } 43 dp[i]=INF; 44 for(int j=0;j<p.size();j++){ 45 int res=p[j]-1; 46 dp[i]=min(dp[i],dp[res]+1); 47 } 48 //cout<<dp[n]<<endl; 49 } 50 cout<<dp[n]<<endl; 51 } 52 return 0; 53 }
分析:显然我们定义dp[i][j],第1个串移除到第i个,第2个串移除到第j个时,所以dp[i][j]一定是由dp[i-1][j]或者dp[i][j-1]转移过来的,但是我们很难去维护一个串的开始位置和结束位置,所以我们只能在代价函数的地方下功夫,每次新加入一个元素,已经开始的但尚未结束的元素长度都增加1个,这样就可以解决这题了。
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=5000+10; 7 const int INF=1<<30; 8 int T; 9 int c[maxn][maxn],dp[maxn][maxn]; 10 int main() 11 { 12 scanf("%d",&T); 13 getchar(); 14 while(T--){ 15 int n=0,m=0; 16 char a[maxn],b[maxn]; 17 char ch=getchar(); 18 while(ch!=' ') a[++n]=ch,ch=getchar(); 19 ch=getchar(); 20 while(ch!=' ') b[++m]=ch,ch=getchar(); 21 int p[maxn],q[maxn]; 22 for(int i=1;i<=n;i++) p[i]=a[i]-'A'; 23 for(int i=1;i<=m;i++) q[i]=b[i]-'A'; 24 int sp[50],sq[50],ep[50],eq[50]; 25 for(int i=0;i<50;i++){ 26 sp[i]=sq[i]=INF; 27 ep[i]=eq[i]=0; 28 } 29 for(int i=1;i<=n;i++){ 30 sp[p[i]]=min(sp[p[i]],i); 31 ep[p[i]]=i; 32 } 33 for(int i=1;i<=m;i++){ 34 sq[q[i]]=min(sq[q[i]],i); 35 eq[q[i]]=i; 36 } 37 for(int i=0;i<=n;i++){ 38 for(int j=0;j<=m;j++){ 39 if(!i&&!j) continue; 40 int v1=INF,v2=INF; 41 if(i) v1=dp[i-1][j]+c[i-1][j]; 42 if(j) v2=dp[i][j-1]+c[i][j-1]; 43 dp[i][j]=min(v1,v2); 44 if(i){ 45 c[i][j]=c[i-1][j]; 46 if(sp[p[i]]==i&&sq[p[i]]>j) c[i][j]++; 47 if(ep[p[i]]==i&&eq[p[i]]<=j) c[i][j]--; 48 }else if(j){ 49 c[i][j]=c[i][j-1]; 50 if(sq[q[j]]==j&&sp[q[j]]>i) c[i][j]++; 51 if(eq[q[j]]==j&&ep[q[j]]<=i) c[i][j]--; 52 } 53 } 54 } 55 printf("%d ",dp[n][m]); 56 } 57 return 0; 58 }
分析:区间DP,我们定义dp[i][j]为从第i个划分点到第j个划分点的最优切分方案,则dp[i][j]=min(dp[i][k]+dp[k][j]+a[j]-a[i]),i<k<j,注意a[0]=0,a[n+1]=L,这样我们可以通过类似矩阵链乘的方法解决这道题目
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=1000+10; 7 const int INF=1<<30; 8 int L,n; 9 int a[maxn]; 10 int dp[maxn][maxn]; 11 int main() 12 { 13 while(cin>>L) 14 { 15 if(L==0) break; 16 cin>>n; 17 for(int i=1;i<=n;i++) 18 cin>>a[i]; 19 a[0]=0;a[++n]=L; 20 for(int i=0;i<=n;i++) 21 dp[i][i]=0; 22 for(int l=1;l<=n;l++){ 23 int i,j,k; 24 for(i=0;i<=n-l;i++){ 25 j=i+l; 26 int minx=INF; 27 for(k=i+1;k<j;k++){ 28 int temp=dp[i][k]+dp[k][j]+a[j]-a[i]; 29 minx=min(temp,minx); 30 } 31 if(minx!=INF) 32 dp[i][j]=minx; 33 } 34 } 35 printf("The minimum cutting is %d. ",dp[0][n]); 36 } 37 return 0; 38 }
分析:设一个串s需要增加d(s)个括号,则有如下两种情况:
(1)如果s是(s')或者[s'],则d(s)转移到d(s')
(2)如果s有至少两个字符,则可以分成AB,d(s)=d(A)+d(B)
记住不管是否满足第一条,都要进行第二条转移,否则比如[][]就会需要2个了,实际只需要0个
设dp[i][j]表示s[i]~s[j]最少需要添加几个,注意我们要打印方案,怎么打印呢?我们在打印的时候重新检查一下哪个决策最好
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 using namespace std; 6 const int maxn=100+10; 7 const int INF=1<<30; 8 char s[maxn]; 9 int T; 10 int dp[maxn][maxn]; 11 bool Match(char a,char b){ 12 if((a=='('&&b==')')||(a=='['&&b==']')) 13 return true; 14 return false; 15 } 16 void dfs(int i,int j){ 17 if(i>j) return; 18 if(i==j){ 19 if(s[i]=='('||s[i]==')') printf("()"); 20 else printf("[]"); 21 return; 22 } 23 int ans=dp[i][j]; 24 if(Match(s[i],s[j])&&ans==dp[i+1][j-1]){ 25 printf("%c",s[i]); 26 dfs(i+1,j-1); 27 printf("%c",s[j]); 28 return; 29 } 30 for(int k=i;k<j;k++){ 31 if(ans==dp[i][k]+dp[k+1][j]){ 32 dfs(i,k); 33 dfs(k+1,j); 34 return; 35 } 36 } 37 } 38 int main() 39 { 40 cin>>T; 41 getchar(); 42 while(T--){ 43 getchar(); 44 int n=0; 45 char ch=getchar(); 46 while(ch!=' ') s[++n]=ch,ch=getchar(); 47 for(int i=1;i<=n;i++){ 48 dp[i][i]=1; 49 dp[i+1][i]=0; 50 } 51 for(int l=1;l<n;l++){ 52 int i,j,k; 53 for(i=1;i<=n-l;i++){ 54 j=i+l; 55 dp[i][j]=INF; 56 if(Match(s[i],s[j])) dp[i][j]=min(dp[i][j],dp[i+1][j-1]); 57 for(k=i;k<j;k++) 58 dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]); 59 } 60 } 61 //cout<<dp[1][n]<<endl; 62 dfs(1,n); 63 cout<<endl; 64 if(T) cout<<endl; 65 } 66 return 0; 67 }
分析:关于这题的状态转移没什么好说的,参看入门经典第二版,主要讲讲几何过程。为什么需要判断i和j是否为对角戏呢?主要有两个原因,对于凸多边形,因为i<k<j,所以j必须比i大2以上,因此必位对角线。但是对于凹多边形,我们判断是否为对角形,主要是不能有其他点位于三角形内部,否则无法进行计算。如图所示
1 #include "iostream" 2 #include "cstdio" 3 #include "cstring" 4 #include "string" 5 #include "cmath" 6 using namespace std; 7 const int maxn=100; 8 const double INF=1<<30; 9 int T,n; 10 int x[maxn],y[maxn]; 11 double dp[maxn][maxn]; 12 double area(int a,int b,int c){ 13 double res=(double)(1.0/2)*(x[a]*y[b]+x[c]*y[a]+x[b]*y[c]-x[c]*y[b]-x[b]*y[a]-x[a]*y[c]); 14 res=fabs(res); 15 return res; 16 } 17 double judge(int a,int b,int c){ 18 for(int i=1;i<=n;i++){ 19 if(i==a||i==b||i==c) continue; 20 double res=area(a,b,i)+area(a,c,i)+area(b,c,i)-area(a,b,c); 21 res=fabs(res); 22 if(res<=0.01) return false; 23 } 24 return true; 25 } 26 int main() 27 { 28 scanf("%d",&T); 29 while(T--){ 30 scanf("%d",&n); 31 for(int i=1;i<=n;i++) 32 scanf("%d%d",&x[i],&y[i]); 33 for(int l=2;l<n;l++){ 34 int i,j,k; 35 for(i=1;i<=n-l;i++){ 36 dp[i][i+1]=0.0; 37 j=i+l; 38 dp[i][j]=INF; 39 for(k=i+1;k<j;k++){ 40 if(judge(i,j,k)){ 41 dp[i][j]=min(dp[i][j],max(max(dp[i][k],dp[k][j]),area(i,j,k))); 42 } 43 } 44 } 45 //printf("%.1lf ",dp[1][n]); 46 } 47 printf("%.1lf ",dp[1][n]); 48 } 49 return 0; 50 }