【经典dp】 poj 3671
开一个dp[30010][3]的数组
其中dp[i][j]表示把第i个数改成j最少要花多少次
那么状态转移方程就列出来了:
令a=1 j!=a[i]
0 j==a[i]
那么dp[i][1]=dp[i-1][1]+a;
dp[i][2]=min(dp[i-1][1],dp[i-1][2])+a;
那么根据空间优化原理,我们的第一维的i不过是为了递推,那么可以优化掉。
不过要注意的是,dp【2】的状态要先求,否则测试数据会是3,因为沿用了本次的dp【1】,而非上次的dp【1】。
#include <iostream> #include <cstdio> #include <iostream> #include <cstdio> #include <memory.h> using namespace std; const int maxn=30002; //int dp[maxn][3]; int dp[maxn]; int cow[maxn]; int main() { // freopen("in.txt","r",stdin); int n; cin >> n; for(int i=1;i<=n;i++) { cin >> cow[i]; int flag; if(cow[i]==2) flag=0; else flag=1; //dp[i][2]=min(dp[i-1][1],dp[i-1][2])+flag; dp[2]=min(dp[1],dp[2])+flag; if(flag==0)flag=1; else flag=0; // dp[i][1]=dp[i-1][1]+flag; dp[1]=dp[1]+flag; } //cout << min(dp[n][1],dp[n][2])<< endl; cout << min(dp[1],dp[2])<< endl; return 0; }
左右遍历,计算left【i】+right【i】的max:
#include <iostream> #include <cstdio> #include <iostream> #include <cstdio> #include <memory.h> using namespace std; #define maxn 30002 int s[maxn],l[maxn],r[maxn]; int main() { //freopen("in.txt","r",stdin); memset(s,0,sizeof(s)); memset(l,0,sizeof(l)); memset(r,0,sizeof(r)); int n; cin >> n; for(int i=1;i<=n;++i) { cin >> s[i]; } if(s[1]==1) { l[1]=1; } for(int i=2;i<=n;i++) { if(s[i]==1) { l[i]=l[i-1]+1; } else l[i]=l[i-1]; } if(s[n]==2) { r[n]=1; } for(int i=n-1;i>=0;i--) { if(s[i]==2) { r[i]=r[i+1]+1; } else r[i]=r[i+1]; } int max=0; for(int i=1;i<=n;i++) { if(l[i]+r[i]>max) { max=l[i]+r[i]; } } cout << n-max << endl; return 0; }
还有一个不错的方法,我们先计算1的个数,然后从前遍历,每次判断:是2就自加1,不是就自减1,其中保存min,则为结果。实际上也为dp,每次的i值代表,编号为i的数组前面不可更改的数字1个数。
#include <iostream> #include <cstdio> using namespace std; const int MAX=30000+10; int a[MAX]; //存储输入的串 int main() { //freopen("in.txt","r",stdin); int cnt; //数组元素的个数 int i; int min; //最少要改变的次数 scanf("%d",&cnt); int num1=0; for (i=0;i<cnt;i++) { scanf("%d",&a[i]); if (a[i] == 1) num1++; } min=num1; for (i=0;i<cnt;i++) { (a[i] == 1) ? num1-- : num1++; if (min > num1) min = num1; } printf("%d\n",min); return 0; }