7.29 dp動態規劃
阿新 • • 發佈:2018-07-30
一個 還要 return none class struct one fine 嚴格
接下裏T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N, M <= 1000)代表方格網的大小
接下來N行,每一行M個數,代表a[i][j](1 <= a[i][j] <= 1000)
對於每組測試數據,輸出一個整數代表從(1, 1)走到 (N, M)途徑的格子的最大的和。
接下來T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N <= 1000, 1 <= M <= N)其含義如題目
接下來一行N個整數a[i](1 <= a[i] <= 1e9)
接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)代表序列的長度
接下來一行N個整數a[i](-1000 <= a[i] <= 1000)代表序列a[i]
接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)
接下來一行N個非負整數a[i]代表每一個節點上的一個蘋果的營養價值(0 <= a[i] <= 1e6)
接下來N - 1行,每一行兩個整數u, v代表u, v之間有一條邊(1 <= u, v <= N)
A題:
Description
為了檢驗你上午有沒有好好聽課,於是又了這一題。給你一個N*M的方格網,左上角為(1,1)右下角為(N, M),每個方格中有一個數a[i][j],剛開始你在位置(1, 1)你每次可以往下走或者往右走一步,你需要確定一種走的方案,最後走到(N, M),使得途徑格子的數的和最大。Input
輸入的第一行一個整數T(T<= 5)代表測試數據的組數接下裏T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N, M <= 1000)代表方格網的大小
接下來N行,每一行M個數,代表a[i][j](1 <= a[i][j] <= 1000)
Output
Sample Input
1
2 2
100 1
50 1
Sample Output
151
很好列出狀態轉移方程 註意邊界情況
#include <bits/stdc++.h> using namespace std; int a[1010][1010]; long long dp[1010][1010]; int main() { int t; cin>>t; while(t--) { int n,m; scanf("%d%d",&n,&m);View Codeint i,j; for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { scanf("%d",&a[i][j]); } } for(i=1;i<=n;i++) { for(j=1;j<=m;j++) { if(i==1&&j==1)dp[i][j]=a[i][j];else if(i==1)dp[i][j]=a[i][j]+dp[i][j-1]; else if(j==1)dp[i][j]=a[i][j]+dp[i-1][j]; else dp[i][j]=a[i][j]+max(dp[i-1][j],dp[i][j-1]); } } printf("%lld\n",dp[n][m]); } return 0; }
B題:
Description
averyboy又遇到麻煩了。他的老板給了他一個問題,如果他不能解決,老板將會開除他。老板給的問題如下,給你一個序列a[1]~a[N]和一個數M你需要從這N個數選出M個數組成一個嚴格遞增的序列,問你一共有多少種選法?averyboy不想被開除,你能幫助他嗎?Input
輸入的第一行為一個整數T(T <= 100)代表測試數據的組數接下來T組測試數據
每組測試數據第一行為兩個整數N, M(1 <= N <= 1000, 1 <= M <= N)其含義如題目
接下來一行N個整數a[i](1 <= a[i] <= 1e9)
Output
對於每組測試數據輸出一個整數代表從N個數中選出M個數組成嚴格遞增序列的選法。因為答案可能很大,所以最後你需要對1e9 + 7取模後輸出Sample Input
2
3 2
1 2 3
3 2
3 2 1
Sample Output
3
0
HINT
第一組測試數據可以選1,2或者,1,3或者2,3一共三種選法
此題很難,據說是CCPC原題,要用到動態規劃,樹狀數組,離散化
樹狀數組求逆序對的思想,把值當位置,可求出第i個元素前面比a[i]小的元素個數,在求和上把復雜度從n降到logn
因為a[i]值很大,開不了那麽大的數組,所以離散化用相對大小代表值
#include <bits/stdc++.h> using namespace std; #define LL long long const int maxn=1010; const int mod=1e9+7; LL tree[maxn][maxn];//樹狀數組,代表區間和 LL dp[maxn][maxn];//dp[i][j]表示考慮到第i個數,且以a[i]結尾,長度為j的遞增序列個數 int a[maxn]; int n,m; struct node { int value; int id; bool operator<(const node &res)const { if(value==res.value)return id>res.id;//把值相同的編號按從大到小排,編號小的放到後面,這樣算前綴和的時候就不會把值相等編號又比它小的算進去 else return value<res.value;//按值從小到大排序 //例如 5 5 8 ,算第二個5的時候就不會把第一個5算進去,算第一個5的時候後面那個5還沒考慮進來呢 } }Node[maxn]; int Rank[maxn];//離散化後表示相對位置大小的數組(第一小,第二小...)把值的大小轉化為相對位置的大小 int lowbit(int x) { return x&(-x); } void update(int loc,int d,int value) { for(int i=loc;i<=n;i+=lowbit(i)) { tree[i][d]=(tree[i][d]+value)%mod;//更新操作可以這樣理解 將loc位置上的值為0元素改成了value,則包含這個位置的所有區間和tree[i]都要加上value } //關於哪個區間含有這個元素有詳細的證明 } LL query(int loc,int d) { LL ans=0; for(int i=loc;i>=1;i-=lowbit(i))//查詢操作是查詢從1到loc的前綴區間和 { ans=(tree[i][d]+ans)%mod; } return ans; } int main() { int t; cin>>t; while(t--) { scanf("%d%d",&n,&m); int i,j; for(i=1;i<=n;i++) { scanf("%d",&Node[i].value); Node[i].id=i; } sort(Node+1,Node+n+1);//將元素按從小到大排好序 for(i=1;i<=n;i++)//離散化 { Rank[Node[i].id]=i; //Node已經排好序了,i即代表Node的相對位置大小 } //例如,第一個元素,它在未排序前的編號為Node[1].id,輸入編號,即可知道它現在的值(相對位置大小,可代表值) for(i=1;i<=n;i++) //以a[i]為終點。考慮到第i個數 { dp[i][1]=1;//終點為i,長度為1的肯定只有它本身這一個啊 update(Rank[i],1,1);//把長度為1且個數為1的更新進去 for(j=2;j<=min(m,i);j++)//從長度為2的開始遍歷,考慮i之前的數,長度肯定小於i,並且只考慮到長度為m就夠了 { LL temp=query(Rank[i]-1,j-1);//以剛好比a[i]小的數結尾,長度為j-1的遞增序列有多少個 dp[i][j]=(dp[i][j]+temp)%mod; update(Rank[i],j,dp[i][j]); } } LL ans=0; for(i=1;i<=n;i++) { ans=(ans+dp[i][m])%mod; } printf("%lld\n",ans); memset(dp,0,sizeof dp); memset(tree,0,sizeof tree); } return 0; }View Code
C題:
Description
不僅天外天喜歡子區間,averyboy也非常喜歡子區間。現在天外天給averyboy一個長度為N的序列a[1]~a[N],天外天讓averyboy找出一個子區間[l, r]使得這個子區間數的和要比其他子區間數的和要大Input
第一行一個整數T(T <= 10)代表測試數據的組數接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)代表序列的長度
接下來一行N個整數a[i](-1000 <= a[i] <= 1000)代表序列a[i]
Output
對於每組測試數據,輸出一個整數,代表最大的子區間和。Sample Input
2
3
1 -100 3
4
99 -100 98 2
Sample Output
3
100
HINT
第一組測試樣例,選擇區間[3,3]和為3最大,第二組測試樣例選擇區間[3, 4]和為98 + 2 = 100最大
此題兩種解法,可以用線段樹,也可以dp
線段樹求區間最大連續和
#include <bits/stdc++.h> #include<algorithm> using namespace std; const int maxn=1e5+10; int a[maxn]; struct node { int L,R; long long ms,rs,ls,s; }Node[maxn<<2]; void pushup(int i) { Node[i].ms=max(Node[i<<1].ms,Node[(i<<1)|1].ms); Node[i].ms=max(Node[i].ms,Node[i<<1].rs+Node[(i<<1)|1].ls); Node[i].ls=max(Node[i<<1].ls,Node[i<<1].s+Node[(i<<1)|1].ls); Node[i].rs=max(Node[(i<<1)|1].rs,Node[i<<1].rs+Node[(i<<1)|1].s); Node[i].s=Node[i<<1].s+Node[(i<<1)|1].s; return; } void build(int i,int l,int r) { Node[i].L=l; Node[i].R=r; if(r==l) { Node[i].s=a[l]; Node[i].ms=a[l]; Node[i].ls=a[l]; Node[i].rs=a[l]; return; } int mid=(l+r)>>1; build(i<<1,l,mid); build((i<<1)|1,mid+1,r); pushup(i); } long long queryR(int i,int l,int r) { if(Node[i].L==l&&Node[i].R==r) { return Node[i].rs; } int mid=(Node[i].L+Node[i].R)>>1; if(r<=mid) return queryR(i<<1,l,r); else if(l>mid) return queryR((i<<1)|1,l,r); else { long long lans=queryR(i<<l,l,mid); long long rans=queryR((i<<1)|1,mid+1,r); return max(rans,lans+Node[(i<<1)|1].s); } } long long queryL(int i,int l,int r) { if(Node[i].L==l&&Node[i].R==r) { return Node[i].ls; } int mid=(Node[i].L+Node[i].R)>>1; if(r<=mid) return queryL(i<<1,l,r); else if(l>mid) return queryL((i<<1)|1,l,r); else{ long long lans=queryL(i<<l,l,mid); long long rans=queryL((i<<1)|1,mid+1,r); return max(lans,rans+Node[i<<1].s); } } long long query(int i,int l,int r) { if(Node[i].L==l&&Node[i].R==r) { return Node[i].ms; } int mid=(Node[i].L+Node[i].R)>>1; if(r<=mid) return query(i<<1,l,r); else if(l>mid) return query((i<<1)|1,l,r); else { long long lans=query(i<<1,l,mid); long long rans=query((i<<1)|1,mid+1,r); long long ans=max(lans,rans); return max(ans,queryR(i<<1,l,mid)+queryL((i<<1)|1,mid+1,r)); } } int main() { int t; cin>>t; while(t--) { int n; scanf("%d",&n); int i; for(i=1;i<=n;i++) { scanf("%d",&a[i]); } build(1,1,n); long long ans=query(1,1,n); printf("%lld\n",ans); } return 0; }View Code
dp
#include <bits/stdc++.h> using namespace std; const int maxn = 1000 + 10; typedef long long LL; const LL mod = 1e9 + 7; int N, M; int a[maxn]; LL Tree[maxn][maxn]; LL dp[maxn][maxn];//dp[i][j]表示考慮到第i個數,且以第a[i]個數結尾,長度為j的遞增序列個數 struct node{ int value; int id; bool operator <(const node &res) const{ if(value == res.value) return id > res.id; else return value < res.value; } }Node[maxn]; int Rank[maxn]; void init() { memset(Tree, 0, sizeof(Tree)); memset(dp, 0, sizeof(dp)); } int lowbit(int x) { return x&(-x); } void add(int loc, int d, LL value) { for(int i = loc; i <= N; i += lowbit(i)) { Tree[i][d] = (Tree[i][d] + value) % mod; } } LL get(int loc, int d) { LL ans = 0; for(int i = loc; i >= 1; i -= lowbit(i)) { ans = (ans + Tree[i][d]) % mod; } return ans; } int main() { freopen("data.in", "r", stdin); freopen("data.out", "w", stdout); int T; scanf("%d", &T); while(T--) { scanf("%d%d", &N, &M); init(); for(int i = 1; i <= N; i++) { scanf("%d", &Node[i].value); Node[i].id = i; } sort(Node + 1, Node + N + 1); for(int i = 1; i <= N; i++) { Rank[Node[i].id] = i; } for(int i = 1; i <= N; i++) { dp[i][1] = 1; add(Rank[i], 1, 1); for(int j = 2; j <= min(M, i); j++) { LL temp = get(Rank[i] - 1, j - 1); dp[i][j] = (dp[i][j] + temp) % mod; add(Rank[i], j, dp[i][j]); } } LL ans = 0; for(int i = 1; i <= N; i++) { ans = (ans + dp[i][M]) % mod; } printf("%lld\n", ans); } return 0; }View Code
D題:
averyboy家有一棵蘋果樹。把這棵蘋果樹看成一個由N(編號為1~N)個節點組成的以1號節點為根的有根樹。每個節點上有一個蘋果,每個蘋果也有一個營養價值a[i]。現在averyboy想知道以每個節點為根的子樹上營養價值為奇數的節點的個數。Input
輸入第一行為一個整數T(T <= 5)代表測試數據的組數接下來T組測試數據
每組測試數據第一行為一個整數N(1 <= N <= 1e5)
接下來一行N個非負整數a[i]代表每一個節點上的一個蘋果的營養價值(0 <= a[i] <= 1e6)
接下來N - 1行,每一行兩個整數u, v代表u, v之間有一條邊(1 <= u, v <= N)
Output
對於每組測試數據,輸出一行N個數,第i個數代表以第i節點為根的子樹(子樹包括自己)上蘋果營養價值為奇數的個數Sample Input
2
3
1 2 3
1 2
2 3
3
1 1 1
1 2
2 3
Sample Output
2 1 1
3 2 1
HINT
在第一組樣例中,以1為根的子樹包括節點1,2,3但是由於2號節點上的蘋果營養價值為2不是奇數,所以以1為根的子樹上一共有2個營養價值為奇數的蘋果。以2為根的子樹包括節點2, 3,所以只有1個營養價值為奇數的蘋果.以3為根的子樹就是3自身,所以也只有1個營養價值為奇數的蘋果。所以最後輸出2 1 1
此題就是樹狀dp,用前向星存圖即可
#include <bits/stdc++.h> using namespace std; const int maxn=1e5+10;//註意這裏的maxn應該開題目給的雙倍,因為邊是雙向的,加邊還要開更多 int a[maxn]; int dp[maxn]; bool visit[maxn]; int n; int head[maxn]; //head[i]表示以i為起點的最後一條邊的編號; struct edge { int to;//這條邊的終點 int last; //與自己起點相同的上一條邊的編號 }Edge[maxn*2];//邊數組 int cnt; //記錄當前邊的編號 void add(int u,int v)//加邊 //起點u,終點v;; { Edge[cnt].to=v; Edge[cnt].last=head[u];//現在是要把編號為cnt的邊加進來, head[u]=cnt++;//現在cnt邊加進來了,cnt邊為以u為起點的最後一條邊 } void dfs(int root)//這棵樹的根節點 { if(a[root]&1)dp[root]=1;//根節點自己 else dp[root]=0; visit[root]=true; //節點已訪問過 for(int i=head[root];i!=-1;i=Edge[i].last)//把節點當起點, { int v=Edge[i].to;//子節點是終點 if(!visit[v]) { dfs(v);//把子節點當根節點去找它的子節點 dp[root]+=dp[v];//根節點的子節點數加上它的子節點的子節點數 } } } int main() { int t; cin>>t; while(t--) { scanf("%d",&n); int i; for(i=1;i<=n;i++)scanf("%d",&a[i]); for(i=1;i<=n;i++)head[i]=-1; cnt=1; for(i=1;i<=n-1;i++) { int u,v; scanf("%d%d",&u,&v); add(u,v); add(v,u); } memset(visit,false,sizeof visit);//記得一定要memset dfs(1); for(i=1;i<=n;i++)printf("%d ",dp[i]); printf("\n"); } return 0; }View Code
7.29 dp動態規劃