【考试题 - sequence】DP

没有测试数据,但是和正解对拍了 400 多组数据都过了.   

手画一下发现每个数如果变化的话只有 3 种情况:$-1,0,1$.   

感性理解的话一个数变小的话对前面更优,对后面更劣.       

但是我们发现不可能存在一个数使得该数变小后会导致后面的数也变小.  

这是因为如果该数变小的话说明该数和前缀最小值已经构成 $max$,而如果后面的数大于当前数的话显然只需要分析前缀最小值和后面的数就行了.     

变小的情况这么分析,变大的情况同理.    

令 $f[i][j]$ 表示做到 $i$ 位,前缀最小值是原前缀最小值+$j$,时间复杂度为 $O(3^2 n)$.     

这道题的数据并不好造,因为如果值域过于分散的话答案就是 1.     

所以我们可以生成一个长度为 $10^5$ 的数组,然后数组中的每个元素的值域为 $[-100,100]$.   

这么造出来的数据效果不错,答案一般能超过 $10^3.$   

代码:  

#include  
#include 
#include    
#define N 1000009  
#define ll long long 
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;    
const int con[]={-1,0,1};  
const int pri[]={1,0,1};  
const int inf=1000000003;    
int dp[N][3],mi[N],val[N],n; 
int get(int x) { 
	if(x==-1) return 0;  
	if(x==0)  return 1; 
	if(x==1)  return 2;  
	return 23; 
}
int main() { 
	// setIO("input");   
	scanf("%d",&n);    
	mi[0]=inf;     
	int fax=0; 
	for(int i=1;i<=n;++i) { 
		scanf("%d",&val[i]);  
		mi[i]=min(mi[i-1],val[i]);    
		fax=max(fax,val[i]-mi[i]);  
	}         
	// 存好前缀最小值.   	    
	memset(dp,0x3f,sizeof(dp));  
	dp[0][1]=0;  
	for(int i=1;i<=n;++i) {    
		for(int x=0;x<3;++x)   
			for(int y=0;y<3;++y) {    
				if(val[i]+con[x]-(mi[i-1]+con[y])>=fax) {   
					continue;  
				}
				int cur=min(mi[i-1]+con[y],val[i]+con[x]);              
				int p=get(cur-mi[i]);        
				dp[i][p]=min(dp[i][p],dp[i-1][y]+pri[x]);  
			}     
	}    	    
	int ans=inf;  
	for(int i=0;i<3;++i) { 
		ans=min(ans,dp[n][i]);  
	}  
	printf("%d\n",ans);   
	return 0;
}

  

对拍:  

#include  
#define N 1000009  
using namespace std;
int a[N];  
void gen() { 
    FILE *fp=fopen("input.in","w");   
	int n=100000;  
	a[1]=1,a[n]=9;  
	for(int i=2;i input.out");  
        system("b.exe < input.in > de.out");   
        if(system("fc input.out de.out > FC.out"))  { 
            printf("WA\n"); 
            exit(0); 
        }   
        else {  
            printf("AC %d\n",++cnt);  
        }
    }
    return 0;     
}

  

你可能感兴趣的:(【考试题 - sequence】DP)