动态规划代码总整理

\(1.\) 线性dp

最长单调递增子序列

signed main(){
	int n,a[105],m=0; 
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	int dp[105]={0};
	dp[1]=1;
	for(int i=2;i<=n;i++){
		int k=0;
		for(int j=1;j<i;j++){
			if(a[i]>a[j]&&dp[j]>k){
				k=dp[j];
			}
		}
		dp[i]=k+1;
		if(m<dp[i]){
			m=dp[i];
		}
	}
	printf("%d",m);
	return 0;
}

\(2.\) 背包dp

(1) 部分背包

struct Goods{   
    int w;   
    int v;   
    double p; 
}g[1005];   
bool cmp(struct Goods g1,struct Goods g2) return g1.p<g2.p; 
signed main()  {  
    int i,n,C;  
    double sum,val=0;
    scanf("%d",&n);  
    for(i=0;i<n;i++){  
        scanf("%d %d",&g[i].w,&g[i].v);  
        g[i].p=(g[i].w*1.0)/g[i].v; 
    }  
    scanf("%d",&C); 
    sort(g,g+n,cmp);  
    for(i=0;i<n;i++) {  
        if(sum+g[i].w<C){  
            sum+=g[i].w;  
            val+=g[i].v;  
        }  
        else break;  
    }  
    val+=((C-sum)*g[i].p);
    printf("%.2lf\n",val);  
    return 0;  
} 

(2) 01背包

signed main(){
	int n,w[105],c[105],dp[105][105]={0},v;
	scanf("%d%d",&n,&v);
	for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&c[i]);
	for(int i=1;i<=n;i++){
		for(int j=v;j>=0;j--){
			if(w[i]<=j){
				dp[j]=max();
			}
		}
	}
	printf("%d",dp[v]);
	return 0;
}

(3) 完全背包

long long dp[30][10005]={0};
signed main(){
    int v,n,a[30];
    scanf("%d%d",&v,&n);
    for(int i=1;i<=v;i++){
        scanf("%d",&a[i]);
    }
    for(int i=1;i<=n;i++)
        if(a[1]<=i&&i%a[1]==0) dp[1][i]=1;
    if(a[1]==1)
        for(int i=1;i<=v;i++)
            dp[i][1]=1;
    for(int i=2;i<=v;i++) dp[i][0]=1;
    for(int i=2;i<=v;i++){
        for(int j=1;j<=n;j++){
            if(j<a[i]) dp[i][j]=dp[i-1][j];
            else dp[i][j]=dp[i-1][j]+dp[i][j-a[i]];
        }
    }
    printf("%lld",dp[v][n]);
    return 0;
}

\(3.\) 区间dp

\(n\) 堆石子,第 \(i\) 堆有 \(a_{i}\) 个,合并时合并相邻两堆,产生的代价为两堆果子的总数,求合并成一堆后的最小代价。

signed main(){
	int n,a[105],sum[105]={0};
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		sum[i]=sum[i-1]+a[i];
	}
	int dp[105][105]={0};
	for(int len=2;len<=n;len++){
		for(int i=1;i+len-1<=n;i++){
			int j=i+len-1;
			dp[i][j]=0x3f3f3f3f;
			for(int k=i;k<j;k++){
				dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1]);
			}
		}
	}
	printf("%d",dp[1][n]);
}

\(4.\) 数位dp

给出一个数 \(n\),\(1 \sim n\) 有多少数包含 \(49\)

数据:\(1 \leq T \leq 10000\),\(1 \leq a_i \leq 2^{63}-1\)

const int Max = 99999;
const int Min = 0;
const int inf = 1e6;
const int mod = 1e9+7;
#define M 1000
#define N 1000
#define ll long long
#define swap(x,y) x^=y^=x^=y
ll dp[20][6];
int digit[20];
ll dfs(int pos,int pre,int sta,bool limit){
	if(pos==-1) return 1;          
	if(!limit&&dp[pos][sta]!=-1) return dp[pos][sta];    
	int up = limit?digit[pos]:9;                              
	ll sum(0); 
	for(int i=0;i<=up;i++){                                 
		if(pre==4&&i==9) continue;                       
		sum += dfs(pos-1,i,i==4,limit&&i==digit[pos]);         
	}
	if(!limit) dp[pos][sta] = sum;                       
	return sum;                                    
}
ll solve(ll a){
	int cnt = 0;                    
	while(a>0){              
		digit[cnt++] = a%10;
		a/=10;
	}
	ll ans = dfs(cnt-1,0,0,true);   
	return ans;
}
signed main(){
	#ifdef LOCAL
	freopen("test.txt","r",stdin);
	#endif
	int T;
	cin >> T;
	memset(dp,-1,sizeof(dp));
	while(T--){
		ll a;
		scanf("%I64d",&a);
		ll ans = solve(a); 
		cout << a+1-ans << endl;
	}
    return 0;
}

\(5.\) 树形dp

(1) 树上最长链

const int N = 1e5 + 10;
int n, root;
vector<int>e[N];
int w[N], du[N];
int gcd(int a, int b) {
	return b == 0 ? a : gcd(b, a % b);
}
int dfs(int u, int ans, int val) {
	int res = 0; res = max(res, ans);
	for (auto& v : e[u]) {
		int x = gcd(val, w[v]);
		if (x == 1)continue;
		else res = max(res, dfs(v, ans + 1, x));
	}
	return res;
}
signed main() {
	cin >> n;
	for (int i = 1; i < n; ++i) {
		int a, b; cin >> a >> b;
		e[a].push_back(b); du[b]++;
	}
	for (int i = 1; i <= n; ++i) cin >> w[i];
	for (int i = 1; i <= n; ++i)
		if (!du[i]) {
			root = i;
			break;
		}
	cout << dfs(root, 1, w[root]) << endl;
	return 0;
}

(2) 树的直径

const int N=2e4+10;
int e[N],ne[N],w[N],h[N],idx;
int f1[N],f2[N];
void add(int a,int b,int c){
    e[idx]=b;w[idx]=c;ne[idx]=h[a];h[a]=idx++;
}
void dfs(int u,int fa){
    for(int i=h[u];~i;i=ne[i]){
        int j=e[i];
        if(j==fa) continue;
        dfs(j,u);
        int d=f1[j]+w[i];
        if(d>f1[u]){
            f2[u]=f1[u];
            f1[u]=d;
        }
        else if(d>f2[u]){
            f2[u]=d;
        }
    }
}
signed main(){
    int n;
    cin>>n;
    memset(h,-1,sizeof h);
    for(int i=1;i<=n-1;i++){
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c);
        add(b,a,c);
    }
    dfs(1,-1);
    int ans=-1e9;
    for(int i=1;i<=n;i++){
        ans=max(ans,f1[i]+f2[i]);
    }
    cout<<ans<<endl;
    return 0;
}

\(6.\) 状压dp

旅行家在 \(n\) 个点 \(m\) 条边的有向图中,从一个点出发,各个城市经历一次且仅一次,然后回到出发城市,求路径最短。

#define M 19
#define INF 0x3f3f3f3f
int n;
int mp[M][M]; 
int dp[1<<11][M];{
    if(dp[s][v] >= 0) return dp[s][v];
    if(s == (1<<n)-1 && v == 0) return dp[s][v] = 0; 
    int ret = INF;
    for(int u = 0;u < n;u++){
        if(!((s >> u) & 1)) 
        ret = min(ret,slove(s|(1<<u),u)+mp[v][u]);
    }
    return dp[s][v] = ret; 
}
int main(){
    while(scanf("%d",&n) == 1 && n){
        for(int i = 0;i < n;i++) fill(mp[i],mp[i]+n,INF);
        for(int i = 0;i < n;i++){
            for(int j = 0;j < n;j++)
                scanf("%d",&mp[i][j]);
        }
        memset(dp,-1,sizeof(dp));
        printf("%d\n",slove(0,0));
    }
    return 0;
} 
posted @ 2022-12-27 22:06  abc_mx  阅读(135)  评论(0)    收藏  举报