题意:
给你n个数,现在要你调整k个数的位置, 使得具有最少的连续段数;
比如 n = 5 , k = 1, 27 28 29 27 30
没调整前 连续段数是5 ,调整为 27 27 28 29 30 后连续段数是4
一开始看了一个错误的标程,害我挂了好久!
source code:
#include <cstdio>/!这个题目的dp好难想到啊, 终于看懂了, 唉, 悲哀!
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 101;
const int maxL = 8;
int a[maxn];
char dp[2][1<<maxL][maxn][maxL+1];
inline void update(char& x, int v){
if(x == -1 || x > v) x = v;
}
bool vis[12];
int main(){
freopen("input.txt", "r", stdin);
int n, k, cas = 0;
while(scanf("%d%d", &n, &k)){
if(n == 0 && k == 0) break;
memset(vis, false, sizeof(vis));
printf("Case %d: ", ++cas);
for(int i = 0; i < n; i ++){
scanf("%d", &a[i]);
a[i] -= 25;
vis[a[i]] = true;
}
memset(dp[0], -1, sizeof(dp[0]));
dp[0][0][0][maxL] = 0;
// dp[i][j][l1][l2] 表示前i个数在删除了l1个数后的状态是j且其是以l2结尾的;
//dp[i][j][l1][l2] = -1表示这种状态是不可达的.
//其中的状态j是表示在前a[0]~a[i]中还剩下数的种类的的状态;
//比如说i = 5, a[0] = 1, a[1] = 3, a[2] = 1, a[4] = '-', a[5] = 4;其中a[4]是删除了.
//则 j = (00011010); (00011010)表示二进制数.
// 状态转移方程是dp[i][j][l1][l2] = min(dp[i-1][k][l1][l2], dp[i-1][j][l1+1][l2]);
//其中 j = k | (1<<a[i]);
for(int i = 0; i < n; i ++){
memset(dp[(i+1)%2], -1, sizeof(dp[(i+1)%2]));
for(int j = 0; j < (1<<maxL); j ++){
for(int l1 = 0; l1 <= k; l1 ++){
for(int l2 = 0; l2 <= maxL; l2 ++){
int tmp = dp[i%2][j][l1][l2];
if(tmp == -1) continue;
// printf("dp[%d][%d][%d][%d] = %d/n", i, j, l1, l2, tmp);
update(dp[(i+1)%2][j|(1<<a[i])][l1][a[i]],
tmp + (a[i] == l2 ? 0 : 1));
update(dp[(i+1)%2][j][l1+1][l2], tmp);
}
}
}
}
int ans = 1000000;
for(int i = 0; i < (1<<maxL); i ++){
for(int j = 0; j <= k; j ++){
for(int l1 = 0; l1 <= maxL; l1 ++){
int tmp = dp[n%2][i][j][l1];
if(tmp == -1) continue;
for(int p = 0; p < maxL; p ++){
if(vis[p] && (((i>>p)&1)==0)) tmp ++;
}
ans = min(ans, tmp);
}
}
}
printf("%d/n/n", ans);
}
}
一款非常好的锁屏APP软件: 酷划, 不仅好用, 而且还能赚钱, 官网地址: http://www.coohua.com