1. 程式人生 > 其它 >MySQL異常 #SQLException: sql injection violation, part alway true condition not allow

MySQL異常 #SQLException: sql injection violation, part alway true condition not allow

動態規劃

目錄

若起始時規定開始狀態的\(j\),可使\(i\)\(n\)到0,輸出\(dp[0][j]\)

可連結區間:可開\(dp[maxn][2]\)表示是否連線,也可一維\((dp[i-1]+a[i],\ dp[i-2]+b[i])\)

\(dp[i]\)定義:要求\(i\)為嚴格連續則定義\(dp[i]\)\(0-i\)且以\(i\)結尾的最優解,若要求\(i\)

可為離散則定義\(dp[i]\)\(0-i\)最優解。

揹包

最好從1開始計算物品,因為0可當作什麼也沒選。

0-1揹包

已知條件有第\(i\)個物品的重量\(w_i\),價值\(v_i\),以及揹包的總容量\(m\)

\(f_{i,j}\) 為在只能放前\(i\)個物品的情況下,容量為\(j\)的揹包所能達到的最大總價值。

\(f_{i,j}=\max(f_{i-1,j},f_{i-1,j-w_i}+v_i)\)

\(fj=\max(f_j,f_{j-w_i}+v_i)\) 從右到左。

完全揹包

與 0-1 揹包的區別僅在於一個物品可以選取無限次,而非僅能選取一次。

\(f_{i,j}=\max_{k=0}^{+\infty}(f_{i-1,j-k\times w_i}+k\times v_i)\)

\(f_{i,j}=\max(f_{i-1,j},f_{i,j-w_i}+v_i)\)

\(fj=\max(f_j,f_{j-w_i}+v_i)\) 從左到右

類似揹包

poj-1015:從\(n\)個物品(有屬性\(x,y\))中選出\(m\)個,要求在\(\min(\mid\sum x-\sum y\mid)\)的前提下

\(\max(\sum x +\sum y)\),三重迴圈,\(j\)當前正在選擇,\(k\)當前差,\(i\)當前可在選,\(dp[j][k]\)當前最大和。

注:輸出時不再按照從小到大選擇\(i\),不能僅使用函式遞迴,應重排序。

參考題解

錯誤程式碼:

path處理錯誤,eg:(1)和(2)同時是\(\max(dp[i][j])\)

只能儲存一個path,由比較句中\(>=\)\(>\)決定選擇後來的或先來的。選擇後將少計算情況。

	for(int j=0; j<m; j++){
		for(int k=0; k<=m*40; k++){
			if(dp[j][k]>=0){
				for(int i=1; i<=n; i++){
					if(dp[j][k]+x[i]+y[i]>dp[j+1][k+x[i]-y[i]]){
						a=j, b=k;
						while(path[a][b]!=i && a>0){
							b-=x[path[a][b]]-y[path[a][b]];
							a--;
						}
						if(a==0){//未選 
							dp[j+1][k+x[i]-y[i]]=dp[j][k]+x[i]+y[i];
							path[j+1][k+x[i]-y[i]]=i;
						}
					}
				}
			}
		}
	}

狀壓DP

例:工作排序,狀態從\(n!\)\(1<<n-1\),外層迴圈\(1<<n-1\),內層迴圈\(n\)

沒有要求整項工作在規定時間做完,所以不能貪心。

#define maxn 20
#define maxm 1<<20
#define inf 1e9
struct node{
	char s[105];
	int d, c;
}a[maxn];
int dp[maxm], t[maxm];
int pre[maxm];
void op(int x){ //注意輸出
	if(x==0) return;
	op(x^(1<<pre[x]));
	printf("%s\n", a[pre[x]].s);
}
int main(){
	int T, n, m;
	cin>>T;
	while(T--){
		scanf("%d", &n);
		for(int i=0; i<n; i++)
			scanf("%s %d %d", a[i].s, &a[i].d, &a[i].c);
        	//name, deadline, continue_time
		m=1<<n;
		dp[0]=0, t[0]=0;
		for(int i=1; i<m; i++){
			dp[i]=(int)inf;
			for(int j=0; j<n; j++){
				int d=a[j].d, c=a[j].c, bit=1<<j;
				if((i&bit)==0) continue;
				int dt=t[i^bit]+c-d;
				if(dt<0) dt=0;
				if(dp[i]>=dp[i^bit]+dt){
					dp[i]=dp[i^bit]+dt;
					t[i]=t[i^bit]+c;
					pre[i]=j;
				}
			}
		}
		printf("%d\n", dp[m-1]);
		op(m-1);
	}
	return 0;
}

區分:每項工作有continue和固定start,則簡單\(dp\)\(O(n^2)\)

區間DP

卡特蘭分法
多邊形切割

將多邊形可按卡特蘭數分法分割,以\(i,j\)為頂點分割線有代價\(f(i,j)\),求最小分法。

初始化\(dp[i][i+1]=cost[i][i+1]=0\)記憶化搜尋即可。\(O(n^3)\)

ll co(int i, int j){
	if(c[i][j]!=-1) return c[i][j];
	node a=h[i], b=h[j];
	return c[i][j]=Abs(a.x+b.x)*Abs(a.y+b.y)%m;
}
ll sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	ll res=inf;
	for(int k=i+1; k<j; k++){
		res=min(res, sol(i, k)+co(i, k)+sol(k, j)+co(k, j));
	}
	return dp[i][j]=res;
}
棧的進出

給定每個時刻棧頂元素,求最小輸入資料個數。

初始化\(dp[i][i]=1\)\(dp[i+1][i]=0\)可不寫,\(dp[i][j]=dp[i][j-1]\)(選新的),\(dp[i][j]=dp[i][k]+dp[k+1][j-1],\ \{k\mid a[k]==a[j]\ \&\&\ i<=k<j\}\)(用以前的)。\(O(n^3)\)

int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	int res=sol(i, j-1)+1;
	if(a[j]==a[j-1]) res=min(res, dp[i][j-1]);
	for(int k=i; k<=j-2; k++){
		if(a[k]==a[j])
		res=min(res, sol(i, k)+sol(k+1, j-1));
	}
	return dp[i][j]=res;
}
括號配對

s應為: ab 或 (a)。

不連續:即())()=4。

其中一種方法:

\(dp[i][j]=dp[i+1][j-1]+2,\ \{a[i]=='(' \&\& a[j]==')'\}\)\(dp[i][j]=dp[i][k]+dp[k+1][j],\ \{k\mid i<=k, k+1<=j\}\)

取最小值即可。

連續:即())()=2。(沒有題目提交過,不保證正確),用棧或者:

\(dp[i][j]=1,\ \{a[i]=='(' \&\& a[j]==')' \&\& dp[i+1][j-1]\}\)\(dp[i][j]=1,\ \{dp[i][k]\&\&dp[k+1][j]\&\&i<k,\ k+1<j\}\)

數列取數
數列兩端取數

\(dp[i][j]=F(f(dp[i+1][j],\ a[i]),\ f(dp[i][j-1],\ a[j]))\)

//eg:score=cnt(選取順序)*a[i],求maxn(score),不用初始化
for(int i=n; i>=1; i--){
	for(int j=i; j<=n; j++){
		dp[i][j]=max(dp[i+1][j]+a[i]*(n-(j-i)),
			dp[i][j-1]+a[j]*(n-(j-i)));
	}
}
數列中間取數

1,n最後取,\(dp[i][j]\)為去完\(i,j\)中間的數最小或最大代價。

\(dp[i][j]=dp[i][k]+dp[k][j]+f[i,k,j]\),最後取\(k\)

int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i+1==j) return dp[i][j]=0;
	int res=(int)inf;
	for(int k=i+1; k<j; k++){
		res=min(res, sol(i, k)+sol(k, j)+a[i]*a[k]*a[j]);
	}
	return dp[i][j]=res;
}
可入棧的取數

已有順序1到n,和代價a[i],個人代價為前面出場人數乘a[i],有一空棧可用於調整順序。

//錯誤:沒有考慮中間出場 
int solwr(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i>=j) return dp[i][j]=0;
	return dp[i][j]=min(s[j]-s[i], a[i]*(j-(i+1)+1))+solwr(i+1, j);
}
int sol(int i, int j){
	if(dp[i][j]!=-1) return dp[i][j];
	if(i>=j) return dp[i][j]=0;
	int res=s[j]-s[i]+sol(i+1, j);
	for(int k=i+1; k<=j; k++){
		res=min(res, sol(i+1, k)+a[i]*(k-(i+1)+1)+
			(s[j]-s[k])*(k-i+1)+sol(k+1, j));
	}
	return dp[i][j]=res;
}
段覆蓋
覆蓋空字串

每次可將\(a\)的一連續段改為某一字元,求將空字串\(a\)修改為\(s\)的最小修改次數。

沒有重複字元:\(dp[j][i]=dp[j+1][i]+1=dp[j][k]+dp[k+1][i]\)

顯然,必須採取先覆蓋兩端邊界為最優解,故:

\(s[j]==s[i]:\ dp[j][i]=dp[j+1][i]=dp[j][i-1]\)

這裡固定\(i\)即可計算。

memset(dp, 0, sizeof(dp));
for(int i=0; i<n; i++){
	for(int j=i; j>=0; j--){
		dp[j][i]=dp[j+1][i]+1;
		for(int k=j+1; k<=i; k++){
			if(s[j]==s[k])
			dp[j][i]=min(dp[j+1][k]+dp[k+1][i], dp[j][i]);
		}
	}
}
覆蓋非空字串

先計算空字串,\(S[i]=min(dp[0][i],\ S[i-1])+1\)

\(a[k]==s[k]:\ S[i]=min(S[i],\ S[k-1]+dp[k+1][i])\)

S[0]=(a[0]==s[0]?0:1);
for(int i=1; i<n; i++){
	S[i]=min(dp[0][i], S[i-1]+1);
	for(int k=0; k<=i; k++){
		if(a[k]==s[k]) S[i]=min(S[i], S[k-1]+dp[k+1][i]);
	}
}

常見

最長上升子序列

\(dp[i]\)初始化為\(1\)\(O(n^2)\)

例:疊加最高立方體。

例:HDU-1257,不是多次查詢最長不嚴格下降子序列,而是直接找最長上升子序列。若\(ans\)大於最長上升子序列,必有一個不在序列中點當起點,該點可來源於前一個起點非嚴格連續下降,故矛盾;若\(ans\)小於最長上升子序列,必有一個在序列中點不當起點,該點為字首最大值,必為起點,故矛盾。

錯誤程式碼:

ans=0;
for(int i=0; i<n; i++){
	dp[i]=1;
	for(int j=0; j<i; j++){
		if(a[j]<a[i]){
			dp[i]=max(dp[i], dp[j]+1); 
			ans=max(ans, dp[i]);//可能永遠不會執行這一句,而ans應為1
		}
	}
}

正確程式碼:

for(int i=0; i<n; i++){
	dp[i]=1;
	for(int j=0; j<i; j++){
		if(a[j]<a[i]){
			dp[i]=max(dp[i], dp[j]+1); 
		}
	}
	ans=max(ans, dp[i]);
}
//or 初始化ans=1

\(o(n\log(n))\)

int a[maxn], b[maxn];
int dp(int n){
	int len, pos;
	b[1]=a[1];
	len=1;//b的size 
	for(int i=2; i<=n; i++){
		if(a[i]>=b[len]) b[++len]=a[i];//非降序列
		else{ //第一個比 a[i] 大的位置
			pos=upper_bound(b+1, b+1+len, a[i])-b;
			b[pos]=a[i];
		}
	}
	return len;
}
最長公共子序列

\(O(n^2)\)

//x,y前各加了一個不相等的無關字元,使字串向後移一位
if(x[i]==y[j]) dp[i][j]=dp[i-1][j-1]+1;
else dp[i][j]=max(dp[i][j-1], dp[i-1][j]);

空間優化

c^=1;//使用亦或翻滾
for(int j=1; j<sy; j++){
	if(x[i]==y[j]) dp[j][c]=dp[j-1][c^1]+1;
	else dp[j][c]=max(dp[j][c^1], dp[j-1][c]);
}
最大對稱矩陣

\(dp[i][j]\)為以\((i,j)\)結尾的最大正方形。

(左上右下對稱)\(dp[i][j]=min(dp[i-1][j-1], \min(k)),\{k\mid a[i-k][j]!=a[i][j-k]\}\)

數列單調代價

可加減調整數列為非嚴格遞增數列,每次調整代價\(abs(pre-now)\)

因為調整後必為原陣列中的數,離散化\(a[i]\)\(dp[i][j]\)為選取第\(i\)個數為\(j\)的最小代價。

\(dp[i][j]=\Delta a+\min(dp[i][k]),\ \{k\mid 1<=k<=j\}\)

一維:\(dp[j]=\Delta a+pm[j]\)\(pm[j]\)為上一行\(\min(dp[k]),\ \{k\mid 1<=k<=j\}\)

sort(b+1, b+1+n);
pm[0]=(ll)inf;
for(int i=1; i<=n; i++){
	for(int j=1; j<=n; j++){
		dp[j]=pm[j]+Abs(a[i]-b[j]);
		pm[j]=min(dp[j], pm[j-1]);
	}
}

斜率DP

eg:HDU-3507 ,給出數列\(a[i]\),列印連續數列段\(k\)\(j\)代價為\((sum[j]-sum[k-1])^2+m\),求列印全部數列最小代價。

比較\(k+1,\ j+1\)哪個更適合和\(i\)放一起,\(k+1<j+1<i\)

\(dp[j]+(s[i]-s[j])^2+m<=dp[k]+(s[i]-s[k])^2+m\)

\(dp[j]+s[j]^2-2\times s[i]\times s[j]<=dp[k]+s[k]^2-2\times s[i]\times s[k]\)

\((dp[j]+s[j]^2)-(dp[k]+s[k]^2)<=2\times(s[j]-s[k])\times s[i]\)

\(K=\frac{(dp[j]+s[j]^2)-(dp[k]+s[k]^2)}{2\times(s[j]-s[k])\times s[i]}<=s[i]\)

即:\(K<s[i]\)時,大的\(j+1\)更合適。

一至三點\(k<j<i\),判斷誰更適合點\(x\)

下凸:\(K_1<K_2\)

1)\(K_1<K_2<s[x]\)\(i\)最優

2)\(K_1<s[x]<K_2\)\(j\)最優

3)\(s[x]<K_1<K_2\)\(k\)最優

故可採用維持佇列下凸,僅比較相鄰點可找到最優,即在\(O(n)\)內完成。

下凹:\(K_1>K_2\)

1)\(s[x]>K_1>K_2\)\(i\)最優

2)\(K_1>s[x]>K_2\)\(k,\ i\)都比\(j\)優,需再比較\(K_{k,\ i}\)\(s[x]\)判斷最終誰優。

3)\(K_1>K_2>s[x]\)\(k\)最優。

故可採用維持佇列下凸,需比較任意兩點可找到最優,即在\(O(n^2)\)內完成。

本題採用\(O(n^2)\)超時,故維護下凸即可。

證明如下操作後,最終中間過程以及最終獲得佇列必為下凸:採用數學歸納。

ll dp[maxn], a[maxn], s[maxn], m;
int q[maxn], n;
ll up(int j, int k){
	return (dp[j]+s[j]*s[j])-(dp[k]+s[k]*s[k]);
}
ll dow(int j, int k){
	return 2*(s[j]-s[k]);
}
ll sol(int j, int i){//選j+1到i
	return dp[j]+m+(s[i]-s[j])*(s[i]-s[j]);
}
int main(){
	while(scanf("%d%lld", &n, &m)!=EOF){
		for(int i=1; i<=n; i++){
			scanf("%lld", &a[i]);
			s[i]=s[i-1]+a[i];
		}
		int st=0, t=0;
		q[t++]=0;
		for(int i=1; i<=n; i++){
			while(st+1<t && up(q[st+1], q[st])<=s[i]*dow(q[st+1], q[st]))
				st++;
			dp[i]=sol(q[st], i);
			while(st+1<t && up(i, q[t-1])*dow(q[t-1], q[t-2])<=
				up(q[t-1], q[t-2])*dow(i, q[t-1])) t--;
			q[t++]=i;
		}
		printf("%lld\n", dp[n]);
	}
}