P1174 打砖块
P1174 打磚塊
題意:
題解:
參考題解:
I_AM_HelloWord
danxmz2006
這兩個博客結合看,大致就能理解
我們只在N處轉移,面對Y類的塊無需決策,因為Y類的塊可以一直打
不同的打磚塊的順序,決定了我們最后的分數情況,因此有個結論:
我們最后一個打的磚塊一定是N類磚塊,除非所有的磚塊都已經打完了
我們從這點出發開始轉移:
對于計算[1,j]列的最優解時:
狀態轉移中我們需要一些輔助變量:
sum1[i][j]表示對于第i列,打到第j行所得分數
sum2[i][j]表示對于第i列,打到第j行同時與此塊相鄰的一連串Y全部打掉能得到的分數(可以理解成在第i列,第j行開始打塊,所能打的最大分)
tot[i][j]:表示對于第i列打到第j行所需要的子彈數量(對于N類磚塊,數量+1,對于Y類不變)
設dp[j][tk][0/1]:表示[1,j]列中,用tk發子彈,最后一發子彈是否在[1,j]列中,0表示在,1表示不在
這樣轉移我們充分考慮了以每一列是否為結尾的情況
轉移1:
//直接繼承上一列的狀態
轉移2:
最后一發子彈在[1,j]中,同時在第j列,因此不在[1,j-1]中
dp[j][k][0]=max(dp[j-1][k-tot[j][i]][1]+sum1[j][i])轉移3:
最后一發子彈在[1,j]中,但不在第j列,因此在[1,j-1]中
轉移4:
最后一發子彈不在[1,j]中,因此[1,j-1]必然沒有,第j列也沒有
代碼:
#include <bits/stdc++.h> #include <unordered_map> #define debug(a, b) printf("%s = %d\n", a, b); using namespace std; typedef long long ll; typedef unsigned long long ull; typedef pair<int, int> PII; clock_t startTime, endTime; //Fe~Jozky const ll INF_ll= 1e18; const int INF_int= 0x3f3f3f3f; void read(){}; template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar) {x= 0;char c= getchar();bool flag= 0;while (c < '0' || c > '9')flag|= (c == '-'), c= getchar();while (c >= '0' && c <= '9')x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();if (flag)x= -x;read(Ar...); } template <typename T> inline void write(T x) {if (x < 0) {x= ~(x - 1);putchar('-');}if (x > 9)write(x / 10);putchar(x % 10 + '0'); } void rd_test() { #ifdef LOCALstartTime= clock();freopen("in.txt", "r", stdin); #endif } void Time_test() { #ifdef LOCALendTime= clock();printf("\nRun Time:%lfs\n", (double)(endTime - startTime) / CLOCKS_PER_SEC); #endif } const int maxn= 210; int cur[maxn], tot[maxn][maxn], a[maxn][maxn]; int b[maxn][maxn]; int sum1[maxn][maxn]; int sum2[maxn][maxn]; int dp[maxn][maxn][2]; int main() {//rd_test();int n, m, k;read(n, m, k);//n行m列k個子彈// memset(a, 0, sizeof(a));// memset(b, 0, sizeof(b));// memset(sum2, 0, sizeof(sum2));// memset(sum1, 0, sizeof(sum1));// memset(cur, 0, sizeof(cur));// memset(tot, 0, sizeof(tot));//memset(cur,0x3f3f3f,sizeof(cur));for (int i= n; i >= 1; i--) {for (int j= 1; j <= m; j++) {read(a[i][j]);char ch= getchar();while (ch < 'A' || ch > 'Z')ch= getchar();if (ch == 'Y')b[i][j]= 1; //對于Y磚標記}}int ans= 0;for (int j= 1; j <= m; j++) {for (int i= 1; i <= n; i++) {if (b[i][j] == 0) //如果是N{cur[j]= i; //記錄了每一列第一次出現N的位置break;}ans+= a[i][j]; //記錄Y的分數}}//先求sum1for (int j= 1; j <= m; j++) {for (int i= cur[j]; i <= n; i++) {sum1[j][i]= sum1[j][i - 1] + a[i][j];sum2[j][i]= sum1[j][i];}}//利用求sum2for (int j= 1; j <= m; j++) {tot[j][cur[j]]= 1; //第j列打到第cur[j]行所需的子彈數量for (int i= cur[j]; i <= n; i++) {int idx= i;while (b[idx + 1][j]) //如果第idx+1行的第j列idx++;//找到這一列連續Y的位置,用于更新sum2sum2[j][i]= sum1[j][idx];//sum2比起sum1多的是連續Y的數量,所以直接更新sum1[j][idx]tot[j][idx + 1]= tot[j][i] + 1;//只需要多一個子彈即可,因為打Y不耗費額外的i= idx;}}//以上為更新sum1和sum2的步驟for (int j= 0; j <= m; j++)dp[j][0][0]= -INF_int;for (int j= 1; j <= m; j++) { //[1,j]列中for (int tk= 1; tk <= k; tk++) { //用了k發子彈dp[j][tk][0]= dp[j - 1][tk][0]; //直接繼承上一列的狀態dp[j][tk][1]= dp[j - 1][tk][1];if(cur[j]==0)continue;//說明這一列都是Y,直接跳過就行 for (int i= cur[j]; i <= n; i++) //枚舉每一行if (!b[i][j] && tk >= tot[j][i]) {//如果第i行,第j列是N,并且子彈數量大于需要的數量//就進行狀態轉移dp[j][tk][0]= max(dp[j][tk][0], dp[j - 1][tk - tot[j][i]][1] + sum1[j][i]);dp[j][tk][0]= max(dp[j][tk][0], dp[j - 1][tk - tot[j][i]][0] + sum2[j][i]);dp[j][tk][1]= max(dp[j][tk][1], dp[j - 1][tk - tot[j][i]][1] + sum2[j][i]);}}}printf("%d\n", dp[m][k][0] + ans);return 0;//Time_test(); } /* 1 5 185 3985 Y 6951 N 2151 Y 1749 N 7413 Y */隊友的代碼:
// Problem: P1174 打磚塊 // Contest: Luogu // URL: https://www.luogu.com.cn/problem/P1174 // Memory Limit: 125 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org)//#pragma GCC target("avx") //#pragma GCC optimize(2) //#pragma GCC optimize(3) //#pragma GCC optimize("Ofast") // created by myq #include<iostream> #include<cstdlib> #include<string> #include<cstring> #include<cstdio> #include<algorithm> #include<climits> #include<cmath> #include<cctype> #include<stack> #include<queue> #include<list> #include<vector> #include<set> #include<map> #include<sstream> #include<unordered_map> #include<unordered_set> using namespace std; typedef long long ll; #define x first #define y second #define int long long typedef pair<int,int> pii;const int N=410;int n,m,k;char g[N][N];int mp[N][N]; int com[N]; bool st[N][N]; int base; int f[N][N][2]; int res=0; int maxv=0; signed main() { cin>>n>>m>>k;int cnt=0;for(int i=1;i<=n;i++){for(int j=1;j<=m;j++){cin>>mp[i][j]>>g[i][j];}}memset(f,-0x3f,sizeof f);f[0][k][0]=0;for(int i=1;i<=m;i++){for(int j=0;j<=k;j++){ f[i][j][0]=f[i-1][j][0];f[i][j][1]=f[i-1][j][1];int sum=0;int sum2=0;bool flag=false;for(int sx=n,sy=i;sx>=1;sx--){sum+=(g[sx][sy]=='Y'?0:-1);if(j==0 && g[sx][sy]=='Y') flag=true;sum2+=mp[sx][sy];//solve 0 if( j-sum<=k){f[i][j][0]=max(f[i][j][0],f[i-1][j-sum][0]+sum2);f[i][j][1]=max(f[i][j][1],f[i-1][j-sum][1]+sum2);}// solve 1if(!flag && j-sum<=k&& g[sx][sy]=='N'){f[i][j][1]=max(f[i][j][1],f[i-1][j-sum][0]+sum2);}}}}for(int i=0;i<=k;i++)res=max(res,f[m][i][1]);cout<<res<<endl;return 0;} /** * In every life we have some trouble * When you worry you make it double * Don't worry,be happy. **/ 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: cf1562 C. Rings
- 下一篇: 主机屋免费空间怎么绑定域名(主机屋免费空