zoukankan      html  css  js  c++  java
  • luogu 【动态规划1】动态规划的引入

    P1216 数字三角形

    每个节点的值只受左上,右上两节点影响。索引从1开始,避免处理边界问题。

    int n,ans,a[1005][1005],dp[1005][1005];
    //pull: dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + dp[i][j]; 
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n;
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= i; j++) cin >> dp[i][j];
    	}
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= i;j++) dp[i][j] = max(dp[i - 1][j - 1], dp[i - 1][j]) + dp[i][j];
    	}
    	for(int i = 1; i <= n; i++) ans = max(ans, dp[n][i]);
    	cout << ans;	
    	return 0;
    }
    
    P1434 滑雪

    一个二维数组,每个数字代表当前点的高度,可以上下左右移动且只可以由高的点移动到低的点,找出最大移动次数 -- 记忆化搜索。

    int n,m,ans,d[110][110],h[110][110],dir[2][4] = {-1,1,0,0,0,0,-1,1};
    //d[x][y]记录(x,y)的最大移动次数 
    int dfs(int x,int y){
    	if(d[x][y]) return d[x][y];
    	d[x][y] = 1;//最小下滑次数为1 
    	for(int i = 0; i < 4; i++){
    		int tx = x + dir[0][i];
    		int ty = y + dir[1][i];
    		if(h[tx][ty] > h[x][y]) d[x][y] = max(d[x][y], dfs(tx, ty) + 1);
    	}
    	return d[x][y];
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n >> m;
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= m; j++) cin >> h[i][j];
    	}
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= m; j++) ans = max(ans, dfs(i,j));
    	}	
    	cout << ans;
    	return 0;
    }
    
    P2196 挖地雷

    n个地窖,每个地窖中有若干个地雷,地窖间的连接情况已给出,设计一个挖出地雷最多的方案。

    • n <= 20,可以直接暴力...
    • 思路同最长上升子序列,dp[i]表示以i为终点时挖到的最大地雷数
    • 枚举i之前的所有的地窖,与i连接时,判断是否可以通过这个地窖挖取更多的地雷,pre数组记录各节点的前驱,更新dp数组的同时,更新相应的前驱。
    int n,m,ans,t,idx,dp[110],link[25][25],w[25],pre[25];
    void print(int i){
    	if(pre[i]) print(pre[i]);
    	cout << i << ' ';
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n;
    	for(int i = 1; i <= n; i++) cin >> w[i];
    	for(int i = 1; i <= n - 1; i++){
    		for(int j = i + 1; j <= n; j++) cin >> link[i][j];
    	}
    	dp[1] = w[1];
    	for(int i = 2; i <= n; i++){
    		dp[i] = w[i];
    		for(int j = 1; j < i; j++){
    			if(link[j][i] && dp[i] < dp[j] + w[i]){
    				dp[i] = dp[j] + w[i];
    				pre[i] = j;
    			}
    		}
    		if(dp[i] > ans){
    			ans = dp[i];
    			t = i;
    		}
    	}
    	print(t);
    	cout << endl << ans;
    	return 0;
    }
    
    P4017 最大食物链计数

    n类生物,m种关系,求最大食物链数量mod 80112002的值。n个点组成了有m条边的DAG --> 入度为0的点到出度为0的点之间路径的个数 --> 可以用拓扑排序 or 记忆化搜索

    • 拓扑排序:
      1. num数组记录各节点权值,初始入度为0的点权值为1,删除这些节点时,将权值加到所有相邻的节点上。
      2. 排序结束后,统计出度为0的节点的权值和
    int n,m,a,b,idx,ans,in[5005],out[5005],h[5005],num[5005];//in out 记录个点出度 入度 
    const int mod = 80112002, kN = 5e5 + 5;
    struct node{
    	int e,ne;
    }edges[kN];
    void add(int a, int b){//s -> e
    	edges[idx].e = b;
    	edges[idx].ne = h[a];
    	h[a] = idx++;
    }
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	memset(h, -1, sizeof h);
        
    	cin >> n >> m;
    	for(int i = 0; i < m; i++){
    		cin >> a >> b;
    		add(a, b);
    		++out[a];
    		++in[b];
    	}
    	queue<int> q;
    	for(int i = 1; i <= n; i++){
    		if(in[i] == 0){//入度为0的点入队 
    			num[i] = 1,q.push(i); 
    		}
    	}
    	while(!q.empty()){
    		int top = q.front();
    		q.pop();
    		//删除top,相邻点入度-1 
    		for(int i = h[top]; i != -1; i = edges[i].ne){
    			--in[edges[i].e];
    			num[edges[i].e] = (num[edges[i].e] + num[top]) % mod;
    			if(in[edges[i].e] == 0) q.push(edges[i].e);  
    		}  
    	}
    	//搜索出度为0的点,计算答案 
    	for(int i = 1; i <= n; i++){
    		if(!out[i]) ans = (ans + num[i]) % mod;
    	}
    	cout << ans;
    	return 0;
    }
    
    • 记忆化搜索
      1. 入度为0的节点:起点,出度为0的节点:终点
      2. 搜索到终点即为一条最长食物链。
      3. dp[i]表示i到终点有多少条路径,搜索时存在dp[i],返回相应值
      4. 搜索所有的起点,结果求和取模。
    P1048 采药

    裸01背包,dp(i,j)表示前i株药材采摘耗时为j的最大价值。由于i状态只与i - 1状态有关,可使用滚动数组优化,dp[i]表示耗时为i时的最大价值.

    #include<iostream>
    #include<algorithm>
    #define endl '
    '
    using namespace std;
    int n,m,ans,dp[1010],t,w;
    //int t[110],w[110];
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n >> m;
    	for(int i = 0; i < m; i++){
    		cin >> t >> w;
    		for(int j = n; j >= t; j--){//大-小枚举,只可使用一次 
    			dp[j]  = max(dp[j], dp[j - t] + w);
    		}
    	}
    	cout << dp[n];
    	/*
    	for(int i = 1; i <= m; i++) cin >> t[i] >> w[i];
    	
    	for(int i = 1; i <= m; i++){
    		for(int j = 0; j <= n; j++){
    			dp[i][j] = dp[i - 1][j];//第i间物品有选 or 不选两种状态
    			if(j >= t[i]) dp[i][j] = max(dp[i][j], dp[i - 1][j - t[i]] + w[i]); 
    		}
    	}
    	cout << dp[m][n];*/
    	
    	return 0;
    }
    
    P1616 疯狂的采药

    完全背包问题,滚动数组优化的01背包中时间从小到大枚举即可。

    	for(int i = 0; i < m; i++){
    		cin >> t >> w;
    		for(int j = t; j <= n; j++) dp[j] = max(dp[j], dp[j - t] + w);
    	}
    
    P1802 五倍经验日

    01背包...,每一个好友都有输 or 赢两种状态,dp[j]表示消耗j个药物获得的最大经验

    • j >= use,win or lose : dp[j] = max(dp[j] + lose, dp[j - use] + win);
    • j < use, lose: dp[j] += lose;
    • 存在use == 0的情况
    int n,x,lose,win,use;
    long long dp[10010];
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n >> x;
    	for(int i = 1; i <= n; i++){
    		cin >> lose >> win >> use;
    		for(int j = x; j >= use; j--) dp[j] = max(dp[j] + lose, dp[j - use] + win);
    		for(int j = use - 1; j >= 0; j--) dp[j] += lose;
    	}
    	cout << dp[x] * 5;
    	return 0;
    }
    
    P1002 过河卒
    //A(0,0) --> B(n, m)的路径条数,其中马所控制的八个点不可以走
    //dp[x][y]表示到达x,y点的路径数;
    //由于卒只能向下向右走:dp[x][y] = dp[x - 1][y] + dp[x][y - 1]
    int n,m,hx,hy,dir[4] = {1,-1,2,-2};
    long long dp[25][25];
    bool book[25][25];
    int main(void){
    	ios::sync_with_stdio(false);
    	cin.tie(0);cout.tie(0);
    	cin >> n >> m >> hx >> hy;
    	book[hx][hy] = true;
    	for(int i = 0; i < 4; i++){
    		for(int j = 0; j < 4; j++){
    			if(i == j) continue;
    			if(abs(dir[i]) == abs(dir[j])) continue;
    			int tx = hx + dir[i];
    			int ty = hy + dir[j];
    			book[tx][ty] = true;
    		}
    	}
    	for(int i = 1; i <= n && !book[i][0]; i++) dp[i][0] = 1;
    	for(int i = 1; i <= m && !book[0][i]; i++) dp[0][i] = 1; 
    	for(int i = 1; i <= n; i++){
    		for(int j = 1; j <= m; j++){
    			if(!book[i - 1][j]) dp[i][j] +=dp[i - 1][j];
    			if(!book[i][j - 1]) dp[i][j] += dp[i][j - 1];
    		}
    	}
    	cout << dp[n][m];
    	return 0;
    }
    
    • 滚动数组优化,待填坑...
  • 相关阅读:
    spoj 3273 Treap
    hdu1074 Doing Homework
    hdu1024 Max Sum Plus Plus
    hdu1081 To the Max
    hdu1016 Prime Ring Problem
    hdu4727 The Number Off of FFF
    【判断二分图】C. Catch
    【01染色法判断二分匹配+匈牙利算法求最大匹配】HDU The Accomodation of Students
    【数轴涂色+并查集路径压缩+加速】C. String Reconstruction
    【数轴染色+并查集路径压缩+加速】数轴染色
  • 原文地址:https://www.cnblogs.com/honey-cat/p/13173419.html
Copyright © 2011-2022 走看看