算法第三章作业
3-3 挖地雷 (25分)
在一个地图上有n个地窖(n≤200),每个地窖中埋有一定数量的地雷。同时,给出地窖之间的连接路径,并规定路径都是单向的,且保证都是小序号地窖指向大序号地窖,也不存在可以从一个地窖出发经过若干地窖后又回到原来地窖的路径。某人可以从任意一处开始挖地雷,然后沿着指出的连接往下挖(仅能选择一条路径),当无连接时挖地雷工作结束。设计一个挖地雷的方案,使他能挖到最多的地雷。
输入格式:
第一行:地窖的个数;
第二行:为依次每个地窖地雷的个数;
下面若干行:
xi yi //表示从xi可到yi,xi<yi。
最后一行为"0 0"表示结束。
输出格式:
k1-k2−…−kv //挖地雷的顺序 挖到最多的雷。
输入样例:
6
5 10 20 5 4 5
1 2
1 4
2 4
3 4
4 5
4 6
5 6
0 0
输出样例:
3-4-5-6 34
定义dp[i]为挖到i点时挖到最多的地雷数。
递归方程式:
dp[i] = dp[j]+a[i];(0 < j < i && path[j][i] = 1)
填表法:
表的维度为一维,表的范围为从1到n,填表顺序为从左到右。
算法复杂度分析:
设n为点数,m为边数,动态规划转移的要遍历每个点以及每条边,记录路径后回溯输出的路径长度不大于n,
那么时间复杂度为O(n+m),要记录每个点的dp[i]以及前驱节点to[i],空间复杂度为O(n),用邻接表存图的复杂度为O(m),
空间复杂度为O(n+m)。
//#pragma GCC optimize(3) #include <bits/stdc++.h> using namespace std; #define lson l,mid,rt<<1 #define rson mid+1,r,rt<<1|1 #define inf 0x3f3f3f3f #define INF 0x7fffffff #define infll 0x3f3f3f3f3f3f3f3f #define il inline #define re register #define pb push_back #define db double #define ll long long #define ull unsigned long long #define pii pair<int,int> #define pll pair<ll,ll> #define puu pair<ull,ull> #define MP make_pair #define lowbit(x) x&(-x) #define fi first #define se second il ll read() { char ch = getchar(); ll p = 1,data = 0; while(ch<'0'||ch>'9') { if(ch == '-')p = -1; ch = getchar(); } while(ch>='0'&&ch<='9') { data = data*10+(ch^48); ch = getchar(); } return p*data; } il ll qpow(ll a,ll b) { ll r = 1; while(b) { if(b&1)r = a*r; a = a*a; b>>=1; } return r; } il ll gcd(ll a,ll b) { if(!a || !b) return (!a)?b:a; while(b ^= a ^= b ^= a %= b); return a; } il ll lcm(ll a,ll b) { return a*b/gcd(a,b); } void exgcd(ll a, ll b, ll &x,ll &y) { if(!b) x = 1, y = 0; else { exgcd(b, a % b, y, x); y -= x * (a / b); } } const int mod = 1e9+7; int a[205],dp[205],to[205]; vector<int> G[205]; int main() { int n = read(); for(int i = 1; i <= n; i++) a[i] = read(); int x = read(),y = read(); while(x && y) { G[y].pb(x); x = read(),y = read(); } for(int i = 1; i <= n; i++) { dp[i] = a[i]; for(auto v:G[i]) { if(dp[i] < dp[v]+a[i]) { dp[i] = dp[v]+a[i]; to[i] = v; } } } int ans = 0,ed = 0; for(int i = 1; i <= n; i++) if(ans < dp[i]) { ans = dp[i]; ed = i; } vector<int> v; while(ed) { v.pb(ed); ed = to[ed]; } reverse(v.begin(),v.end()); int m = v.size(); for(int i = 0; i < m; i++) printf("%d%s",v[i],i!=m-1?"-":"\n"); printf("%d\n",ans); return 0; }
对动态规划的理解:
动态规划要满足子结构的最优性质,无后继性,子问题的重叠性。那么思考用动态规划的方法去解决问题时就要从这三个方面考虑是否满足性质。定义了dp数组后要考虑在可行的时间复杂度来完成转移,并且要把所有情况都考虑到。
结对编程情况:
这次结对编程加强了我观察代码并且debug的能力,和队友讨论合作产生的思想火花也让我受益匪浅。