题目链接:
http://bjutacm.openjudge.cn/lianxi/?page=2
分类目录归档:动态规划
洛谷 P1373 小a和uim之大逃离
自己博客上好像全是代码了,正经的题解还没多少。。。
代码还都是一堆看别人题解写出来的。
https://www.luogu.org/blog/yh1127/solution-p1373
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include<iostream> #include<cstring> #define MOD 1000000007 using namespace std; int f[805][805][17][2],a[805][805]; int n,m,k,ans; int main(){ ios::sync_with_stdio(false); cin>>n>>m>>k; k++; for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ cin>>a[i][j]; f[i][j][a[i][j]][0]=1; } for(int i=1;i<=n;i++)for(int j=1;j<=m;j++)for(int p=0;p<=k-1;p++){ f[i][j][p][1]+=f[i][j-1][(p+a[i][j])%k][0]; f[i][j][p][1]%=MOD; f[i][j][p][1]+=f[i-1][j][(p+a[i][j])%k][0]; f[i][j][p][1]%=MOD; f[i][j][p][0]+=f[i][j-1][(p-a[i][j]+k)%k][1]; f[i][j][p][0]%=MOD; f[i][j][p][0]+=f[i-1][j][(p-a[i][j]+k)%k][1]; f[i][j][p][0]%=MOD; } for(int i=1;i<=n;i++)for(int j=1;j<=m;j++){ ans+=f[i][j][0][1]; ans%=MOD; } cout<<ans; } |
洛谷 P1220 关路灯
我递推啥都不会写垃圾死了,一道对我来说用递推非常难以解决的、难以思考的DP题目被我用记忆化递归解决了,0ms,高兴死我啦!
哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈哈,代码写的那么长。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | #include<iostream> #include<cstring> using namespace std; int n,c; int pos[55],power[55]; int prefixPower[55]; int f[55][55][2]; inline int getEnergy(int l,int r){ return prefixPower[n]-prefixPower[r]+prefixPower[l-1]; } int dp(int l,int r,int d){ if(f[l][r][d]!=-1)return f[l][r][d]; if(l==1&&r==n)return f[l][r][d]=0; if(l==1&&r<n)return f[l][r][d]=getEnergy(l,r)*(d?pos[r+1]-pos[r]:pos[r+1]-pos[l])+dp(l,r+1,1); if(l>1&&r==n)return f[l][r][d]=getEnergy(l,r)*(d?pos[r]-pos[l-1]:pos[l]-pos[l-1])+dp(l-1,r,0); else return f[l][r][d]=min(getEnergy(l,r)*(d?pos[r]-pos[l-1]:pos[l]-pos[l-1])+dp(l-1,r,0),getEnergy(l,r)*(d?pos[r+1]-pos[r]:pos[r+1]-pos[l])+dp(l,r+1,1)); } int main(){ ios::sync_with_stdio(false); memset(f,-1,sizeof(f)); cin>>n>>c; for(int i=1;i<=n;i++)cin>>pos[i]>>power[i]; for(int i=1;i<=n;i++)prefixPower[i]=prefixPower[i-1]+power[i]; cout<<min(dp(c,c,0),dp(c,c,1)); } |
洛谷 P1441 砝码称重
先写了个两层搜索,后来又参考题解改了个搜索+动态规划。
题解:https://www.luogu.org/blog/yeyangrui/solution-p1441
60分搜索:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #include<iostream> #include<algorithm> #include<cstring> #define NINF 0x80000000 using namespace std; int n,m; bool mass[2005]; int weight[25]; bool visited[25]; void dfs(int pos,int num){ if(pos==n+1)return; if(visited[pos]){ dfs(pos+1,num); return; } mass[num+weight[pos]]=true; dfs(pos+1,num+weight[pos]); mass[num]=true; dfs(pos+1,num); } int getMassCounts(){ memset(mass,false,sizeof(mass)); dfs(1,0); int cnter=0; for(int i=1;i<=n*100;i++)if(mass[i])cnter++; return cnter; } int genCombination(int pos,int num){ if(num==m)return getMassCounts(); if(pos>n)return NINF; int maxC=NINF; visited[pos]=true; maxC=max(maxC,genCombination(pos+1,num+1)); visited[pos]=false; maxC=max(maxC,genCombination(pos+1,num)); return maxC; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++)cin>>weight[i]; cout<<genCombination(1,0); } |
100分动规:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include<iostream> #include<algorithm> #include<cstring> #define NINF 0x80000000 using namespace std; int n,m; int f[2005]; int weight[25]; bool visited[25]; int getMassCounts(){ memset(f,0,sizeof(f)); f[0]=1; for(int i=1;i<=n;i++){ if(visited[i])continue; for(int j=2000;j>=0;j--){ if(j+weight[i]<=2000&&f[j]!=0)f[j+weight[i]]=1; } } int cnter=0; for(int i=1;i<=2000;i++)if(f[i])cnter++; return cnter; } int genCombination(int pos,int num){ if(num==m)return getMassCounts(); if(pos>n)return NINF; int maxC=NINF; visited[pos]=true; maxC=max(maxC,genCombination(pos+1,num+1)); visited[pos]=false; maxC=max(maxC,genCombination(pos+1,num)); return maxC; } int main(){ ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++)cin>>weight[i]; cout<<genCombination(1,0); } |
洛谷 P1066 2^k进制数
50分的一个搜索写法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | #include<iostream> #include<algorithm> #include<cstdio> using namespace std; int result[205]; int pow2[10]={1,2,4,8,16,32,64,128,256,512}; int k,w; inline void percolate(){ for(int i=1;i<=203&&result[i]>10000;i++){ result[i+1]+=result[i]/10000; result[i]%=10000; } } inline void dfs(int pos,int num){ if(num==0)return; if((w%k==0?w/k:w/k+1)<pos)return; if(w%k!=0&&(w/k+1)==pos)num=min(pow2[w%k]-1,num); for(int i=1;i<=num;i++)dfs(pos+1,i-1); if(pos!=1){ result[1]+=num; percolate(); } } int main(){ cin>>k>>w; dfs(1,pow2[k]-1); int flag=0; for(int i=204;i>0;i--){ if(result[i])flag++; if(result[i]==0){if(flag)cout<<"0000";} else printf((flag==1?"%d":"%04d"),result[i]); } } |
100分写法:
参考题解:https://www.luogu.org/blog/user50852/solution-p1066
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 | #include<iostream> #include<string> #include<algorithm> #define PINF 0x7fffffff using namespace std; struct bigInt { //For Non-negative Numbers // You must include header **iostream** and **string** before using this class! private: string v; static string & bIsort(string & v) { //each char has reduced '0' for (int i = 0; i < v.length(); i++) { if (v[i] > 9) { if (i == v.length() - 1)v.append(1, v[i] / 10); else v[i + 1] += v[i] / 10; v[i] %= 10; } } return v; } static string lng2str(long long v) { string s = ""; while (v) { s.append(1, v % 10 + '0'); v /= 10; } reverse(s.begin(), s.end()); return s; } static string& trimPre0(string& v3) { while (v3.length() != 0 && (*v3.begin()) == '0')v3.erase(v3.begin()); if (v3.length() == 0)v3 = "0"; return v3; } public: bigInt() { v = "0"; } bigInt(long long n) { v = lng2str(n); } bigInt(string s) { v = s; } bigInt& operator = (const bigInt & bI) { this->v = bI.v; return *this; } bigInt operator + (bigInt bI2) const { string s1 = v, s2 = bI2.v; if (s1.length() > s2.length())swap(s1, s2); reverse(s1.begin(), s1.end()); reverse(s2.begin(), s2.end()); int s1len = s1.length(), s2len = s2.length(); for (int i = 0; i < s1len; i++)s2[i] += s1[i] - '0'; for (int i = 0; i < s2len - 1; i++)if (s2[i] > '9') { s2[i + 1] += (s2[i] - '0') / 10; s2[i] = '0' + (s2[i] - '0') % 10; } if (s2[s2len - 1] > '9') { s2.append(1, '0' + (s2[s2len - 1] - '0') / 10); s2[s2len - 1] = '0' + (s2[s2len - 1] - '0') % 10; } reverse(s2.begin(),s2.end()); return bigInt(s2); } bigInt operator - (bigInt bI2) const { string s1, s2; if ((*this) < bI2) { s1 = v; s2 = bI2.v; } else { s1 = bI2.v; s2 = v; } reverse(s1.begin(), s1.end()); reverse(s2.begin(), s2.end()); int s1len = s1.length(), s2len = s2.length(); for (int i = 0; i<s1len; i++)s2[i] -= s1[i] - '0'; for (int i = 0; i<s2len - 1; i++)if (s2[i]<'0') { s2[i + 1] -= 1; s2[i] += 10; } if (s2[s2len - 1] == '0')s2.erase(s2.end() - 1); reverse(s2.begin(), s2.end()); return bigInt(trimPre0(s2)); } bigInt operator * (bigInt bI2) const { string v1 = v, v2 = bI2.v; if (v2.length() > v1.length())swap(v1, v2); reverse(v1.begin(), v1.end()); reverse(v2.begin(), v2.end()); string v3 = ""; for (int i = 0; i < v1.length(); i++)v1[i] -= '0'; for (int i = 0; i < v2.length(); i++)v2[i] -= '0'; v3.resize(v1.length() + v2.length(), 0); for (int i = 0; i < v2.length(); i++) { for (int j = 0; j < v1.length(); j++) { v3[i + j] += v2[i] * v1[j]; } bIsort(v3); } for (int i = 0; i < v3.length(); i++)v3[i] += '0'; reverse(v3.begin(), v3.end()); trimPre0(v3); return bigInt(v3); } bigInt operator / (long long bI2) const { string v1 = v; for (int i = 0; i < v1.length(); i++)v1[i] -= '0'; long long v2 = bI2; string v3 = ""; long long div = 0; for (int i = 0; i < v1.length(); i++) { div *= 10; div += v1[i]; if (div < v2) { v3.append(1, '0'); continue; } v3.append(lng2str(div / v2)); div %= v2; } return bigInt(trimPre0(v3)); } bool operator < (bigInt bI2) const { if (v.length() != bI2.v.length())return v.length() < bI2.v.length(); for (int i = 0; i < v.length(); i++) { if (v[i] != bI2.v[i])return v[i] < bI2.v[i]; } return false; } friend istream& operator >> (istream& in, bigInt& bI) { in >> bI.v; return in; } friend ostream& operator << (ostream& out, bigInt& bI) { out << bI.v; return out; } }; bigInt ans=0,f[520][520]; int main(){ int k,w; cin>>k>>w; int maxLen=w/k; if(w%k!=0)maxLen++; int radix=1<<k; maxLen=min(maxLen,radix-1); for(int i=1;i<=radix-1;i++)f[1][i]=1; for(int i=2;i<=maxLen;i++){ int l; if(w%k==0)l=PINF; else if(i==maxLen)l=(1<<(w%k))-1; else l=PINF; for(int j=radix-i;j>0;j--){ f[i][j]=f[i][j+1]+f[i-1][j+1]; if(j<=l)ans=ans+f[i][j]; } } cout<<ans; } |
洛谷 P1439 【模板】最长公共子序列
这题不是LCS的裸题啊……。。基本上照抄的题解:
https://pks-loving.blog.luogu.org/junior-dynamic-programming-dong-tai-gui-hua-chu-bu-ge-zhong-zi-xu-lie
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #include<iostream> using namespace std; int a[100005],b[100005],map[100005],f[100005]; int main(){ int n; cin>>n; for(int i=1;i<=n;i++){cin>>a[i];map[a[i]]=i;} for(int i=1;i<=n;i++){cin>>b[i];f[i]=0x7fffffff;} int len=0; f[0]=0; for(int i=1;i<=n;i++){ int l=0,r=len,mid; if(map[b[i]]>f[len])f[++len]=map[b[i]]; else{ while(l<r){ mid=(l+r)>>1; if(f[mid]>map[b[i]])r=mid; else l=mid+1; } f[l]=min(map[b[i]],f[l]); } } cout<<len; } |
洛谷 P1108 低价购买
头一回自己写出来个这么难的动规,小小的激动一下。。
如果直接把100分的代码贴出来,也没啥用。我把分数逐渐增长的所有代码都贴出来,万一自己以后又做到这道题没有思路可作参考。
确实很麻烦。如果直接看100分的会一头雾水,还写不出来注释,思想太复杂了。只可意会。
想看简单题解的OIer可以去洛谷题解上看。
63分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include<iostream> #include<cstring> #include<algorithm> #define ll long long #define pii pair<int,int> #define PINF 0x7fffffff #define NINF 0x80000000 using namespace std; int n; int price[5005]; int dp[5005]; int ways[5005]; bool visited[65537]; int main() { cin >> n; for (int i = 1; i <= n; i++)cin >> price[i]; for (int i = 1; i <= n+1; i++) { int mDpV = 0, cWays = 1; for (int j = 1; j < i; j++) { if (price[j] > price[i]) { if (dp[j] > mDpV) { mDpV = dp[j]; cWays = ways[j]; memset(visited, false, sizeof(visited)); visited[price[j]] = true; } if (dp[j] == mDpV && !visited[price[j]]) { cWays += ways[j]; visited[price[j]] = true; } } } dp[i] = mDpV + 1; ways[i] = cWays; } cout << dp[n+1]-1 << " " <<ways[n+1]; } |
73分:把memset的范围调小了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include<iostream> #include<string> #include<cstring> #include<algorithm> #include<queue> #define ll long long #define pii pair<int,int> #define PINF 0x7fffffff #define NINF 0x80000000 using namespace std; int n; int price[5005]; int dp[5005]; int ways[5005]; bool visited[65537]; int main() { cin >> n; price[0] = 65537; ways[0] = 1; int vmax = 0, vmin = 65537; for (int i = 1; i <= n; i++) { cin >> price[i]; vmax = max(vmax, price[i]); vmin = min(vmin, price[i]); } for (int i = 1; i <= n+1; i++) { int mDpV = dp[0], cWays = 1; for (int j = 1; j < i; j++) { if (price[j] > price[i]) { if (dp[j] > mDpV) { mDpV = dp[j]; cWays = ways[j]; memset(visited + vmin, false, sizeof(int)*(vmax - vmin + 1)); visited[price[j]] = true; } if (dp[j] == mDpV && !visited[price[j]]) { cWays += ways[j]; visited[price[j]] = true; } } } dp[i] = mDpV + 1; ways[i] = cWays; } cout << dp[n+1]-1 << " " <<ways[n+1]; } |
82分:不在每个j处都memset,而是利用版本号的想法,改为在i处更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | #include<iostream> #include<cstring> #include<algorithm> #define ll long long #define pii pair<int,int> #define PINF 0x7fffffff #define NINF 0x80000000 using namespace std; int n; int price[10000]; long long dp[10000]; long long ways[10000]; int visited[65540]; //[price][version()] int main() { cin >> n; int vmax = 0, vmin = 65537; for (int i = 1; i <= n; i++) { cin >> price[i]; vmax = max(vmax, price[i]); vmin = min(vmin, price[i]); } for (int i = 1; i <= n+1; i++) { long long mDpV = 0; long long cWays = 1; memset(visited, 0, sizeof(visited)); for (int j = 1; j < i; j++) { if (price[j] > price[i]) { if (dp[j] > mDpV) { mDpV = dp[j]; cWays = ways[j]; visited[price[j]] = mDpV; } if (dp[j] == mDpV && visited[price[j]]<mDpV) { cWays += ways[j]; visited[price[j]] = mDpV; } } } dp[i] = mDpV + 1; ways[i] = cWays; } cout << dp[n+1]-1 << " " <<ways[n+1]; } |
100分:参考别人题解发现了用后面替代前面的重要问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include<iostream> #include<cstring> #include<algorithm> #define ll long long #define pii pair<int,int> #define PINF 0x7fffffff #define NINF 0x80000000 using namespace std; int n; int price[10000]; long long dp[10000]; //DP 数组 long long ways[10000]; //方法数 数组 long long visited[65540][2]; // [price][0]代表当前最长降序子序列长度,[price][1]代表在此情况下的方法数 int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> price[i]; for (int i = 1; i <= n+1; i++) { long long mDpV = 0; long long cWays = 1; memset(visited, 0, sizeof(visited)); for (int j = 1; j < i; j++) { if (price[j] > price[i]) { //当visited[price[j]][0]有更新时,用后面的替代前面的,因为相应的方法数也会有更新 if (dp[j] == mDpV && visited[price[j]][0]==mDpV) { cWays += ways[j]; cWays -= visited[price[j]][1]; visited[price[j]][1] = ways[j]; } else if (dp[j] == mDpV && visited[price[j]][0]<mDpV) { cWays += ways[j]; visited[price[j]][0] = mDpV; visited[price[j]][1] = ways[j]; } else if (dp[j] > mDpV) { mDpV = dp[j]; cWays = ways[j]; visited[price[j]][0] = mDpV; visited[price[j]][1] = ways[j]; } } } dp[i] = mDpV + 1; ways[i] = cWays; } cout << dp[n+1]-1 << " " <<ways[n+1]; } |
洛谷 P1541 乌龟棋
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | #include<string> #include<cstring> #include<algorithm> #include<cstdlib> #include<cmath> #include<queue> #include<vector> #include<iostream> #include<cstring> using namespace std; int n,m; int a[355],b[6]; int f[45][45][45][45]; int safeF(int i,int j,int k,int l){ if(i<0||j<0|k<0||l<0)return 0; else return f[i][j][k][l]; } int main(){ cin>>n>>m; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=m;i++){ int t; cin>>t; b[t]++; } for(int i=0;i<=b[1];i++)for(int j=0;j<=b[2];j++)for(int k=0;k<=b[3];k++)for(int l=0;l<=b[4];l++){ f[i][j][k][l]=max(max(safeF(i-1,j,k,l),safeF(i,j-1,k,l)),max(safeF(i,j,k-1,l),safeF(i,j,k,l-1)))+a[i+2*j+3*k+4*l+1]; } cout<<f[b[1]][b[2]][b[3]][b[4]]; } |
洛谷 P1880 [NOI1995]石子合并
转移方程是看别人的……
参考:易懂https://www.luogu.org/blog/user45556/solution-p1880
区间动规经典题目?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #include<iostream> #include<string> #include<cstring> #include<algorithm> using namespace std; int n; int arr[205]; int presum[205]; int fMax[205][205], fMin[205][205]; int dfsMax(int l, int r) { if (l == r)return 0; if (fMax[l][r] != 0)return fMax[l][r]; int dIJ = presum[r] - presum[l - 1]; int mmax = 0; for (int k = l; k < r; k++) mmax = max(mmax, dfsMax(l, k) + dfsMax(k + 1, r) + dIJ); return fMax[l][r] = mmax; } int dfsMin(int l, int r) { if (l == r)return 0; if (fMin[l][r] != 0)return fMin[l][r]; int dIJ = presum[r] - presum[l - 1]; int mmin = 0x7fffffff; for (int k = l; k < r; k++)mmin = min(mmin, dfsMin(l, k) + dfsMin(k + 1, r) + dIJ); return fMin[l][r] = mmin; } int main(){ cin >> n; for (int i = 1; i <= n; i++)cin >> arr[i]; for (int i = n + 1; i <= 2*n; i++)arr[i] = arr[i - n]; for (int i = 1; i <= 2 * n; i++)presum[i] = presum[i - 1] + arr[i]; int mmin = 0x7fffffff; for (int i = 1; i <= n; i++) { mmin = min(mmin, dfsMin(i, i + n - 1)); } cout << mmin << endl; int mmax = 0; for (int i = 1; i <= n; i++) { mmax = max(mmax, dfsMax(i, i + n - 1)); } cout << mmax << endl; } |
洛谷 P1020 导弹拦截
又是自己改了半天的一道题,忘了……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | #include<iostream> #include<string> #include<algorithm> using namespace std; int n=1; int arr[100005]; int f[100005]; int f2[100005]; int main() { while (cin >> arr[n])n++; n--; arr[0] = 50001; f[0] = 0; for (int i = 1; i <= n; i++) { int m = f[0]; for (int j = 1; j < i; j++) { if (f[j]>m&&arr[j]>=arr[i]) { m = f[j]; } } f[i] = m + 1; } cout << *max_element(f+1,f+n+1) << endl; arr[0] = 0; f2[0] = 0; for (int i = 1; i <= n; i++) { int m = f2[0]; for (int j = 1; j < i; j++) { if (f2[j]>m&&arr[j]<arr[i]) { m = f2[j]; } } f2[i] = m + 1; } cout << *max_element(f2+1,f2+n+1) << endl; } |