[Codeforces Round #642 (Div. 3)] :
D:
思路:暴力维护优化.
用大顶堆来维护,删除元素同时放入两半区间
注意的是优先队列默认为最大堆,所以cmp重载,符号需要都和我们想要的结果相反.即(> > = <,> < = >).
Code:
typedef tuple<int,int,int> ple;//len,l,r struct cmp { bool operator()(const ple &a,const ple &b) { if(get<0>(a) == get<0>(b)) return get<1>(a) > get<1>(b); return get<0>(a) < get<0>(b); } }; int a[N]; int main() { int t;sd(t); while(t--) { priority_queue<ple,vector<ple>,cmp> Q; int n;sd(n); Q.push(ple(n,1,n)); for(int i=1;i<=n;++i) { int L = get<1>(Q.top()),r = get<2>(Q.top()); Q.pop(); int mid = L+r>>1; a[mid] = i; if(L != r) { int dis1 = mid-L,dis2 = r-mid; Q.push(ple(mid-L,L,mid-1)); Q.push(ple(r-mid,mid+1,r)); } } for(int i=1;i<=n;++i) printf("%d%c",a[i],i==n?'\n':' '); } system("pause"); return 0; }
E:
思路:枚举首位,dp转移.
在k的控制下,首位肯定是[1,k].
然后i+k,.....
在k的控制下,首位肯定是[1,k].
然后i+k,.....
定义:
dp[i][0]为i位置选0时的最小代价.
dp[i][1]为i位置选1时的最小代价.
转移思路:
若i位置为1.
i-k为1,保证[i+k,i]都为0. dp[i][1] = dp[i-k][1]+(中间为0的代价.)
i-k为0,保证[i+k,i]前面的都为0. dp[i][0] = 前面都为0的代价
i位置为0.
i-k为1,保证[i+k,i]都为0. dp[i][1] = dp[i-k][1]+(中间为0的代价)
i-k为0,保证[i+k,i]都为0. dp[i][0] = dp[i-k][0]+(中间为0的代价).
0的删除代价可以前缀和维护.
dp[i][0]为i位置选0时的最小代价.
dp[i][1]为i位置选1时的最小代价.
转移思路:
若i位置为1.
i-k为1,保证[i+k,i]都为0. dp[i][1] = dp[i-k][1]+(中间为0的代价.)
i-k为0,保证[i+k,i]前面的都为0. dp[i][0] = 前面都为0的代价
i位置为0.
i-k为1,保证[i+k,i]都为0. dp[i][1] = dp[i-k][1]+(中间为0的代价)
i-k为0,保证[i+k,i]都为0. dp[i][0] = dp[i-k][0]+(中间为0的代价).
0的删除代价可以前缀和维护.
inline int read() { int x = 0,f = 1;char c = getchar(); while(c < '0' || c > '9'){if(c == '-') f = -1;c = getchar();} while(c >= '0' && c <= '9'){x = (x<<1)+(x<<3)+(c^48);c = getchar();} return x*f; } int dp[N][2],sum[N]; int main() { int t;sd(t); while(t--) { int n,k,j; n = read();k = read(); char s[N];scanf("%s",s+1); sum[0] = 0; for(int i=1;i<=n;++i) { sum[i] = sum[i-1]+(s[i] == '1'?1:0); } int ans = INF; for(int i=1;i<=k;++i) { dp[i][0] = sum[i-1]+(s[i] == '1'); dp[i][1] = sum[i-1]+(s[i] == '0'); for(j=i+k;j<=n;j+=k) { dp[j][1] = min(sum[j-1],dp[j-k][1]+sum[j-1]-sum[j-k])+(s[j] == '0'); dp[j][0] = min(dp[j-k][0],dp[j-k][1])+sum[j-1]-sum[j-k]+(s[j] == '1'); } j -= k; ans = min(ans,min(dp[j][1],dp[j][0])+sum[n]-sum[j]); } pr(ans); } system("pause"); return 0; }
F:
思路:
首先要明白:最优解的情况下最优路径上肯定有最少一个点保存不变.
证明:
对于最优路径[a1,a2,a3...an]:
最大值肯定<=max(a1~an),因为只能减小不能增大.
那么在往所有数肯定都在缩小的范围内变动,那么保持有一个点不动,答案更优
那么,我们可以去枚举不变的那个点:
不变的点确定后,其他的点的高度也可以确定,然后dp转移统计最优的解.
对于dp的转移:
我们确定了一个点x,y不变.
进行二次dp.第一次统计(x,y)到(1,1)上的有影响的点的dp值.
第二统计(x,y)到(n,m)上的有影响的点的dp值。
那么最终结果就是dp[1][1]+dp[n][m]..
注意这里的有影响的点,当(x,y)确定是路径上的点后,那些走不到(x,y)的点或者(x,y)走不到的点不用管.
所以我们dp的时候是
i ~ [x~1],j~[y,1] 和 i~[x,n],j~[y~m].
int n,m; LL mp[105][105],dp[105][105],he[105][105],ans; void slove(int x,int y) { for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) dp[i][j] = INF,he[i][j] = mp[x][y]-(x-i)-(y-j);//先处理出x,y不变时,其他点的高度(曼哈顿距离). dp[x][y] = 0,he[x][y] = mp[x][y]; for(int i=x;i>=1;--i) { for(int j=y;j>=1;--j) { if(i == x && j == y) continue; if(mp[i][j] < he[i][j]) continue;//很显然无法增加高度,所以这种情况下走不了. LL ma1,ma2; if(i+1 <= n)//控制下下标边界 { ma1 = dp[i+1][j]+mp[i][j]-he[i][j];//计算下这个点变成这个高度后的贡献. dp[i][j] = min(dp[i][j],ma1); } if(j+1 <= m) { ma2 = dp[i][j+1]+mp[i][j]-he[i][j]; dp[i][j] = min(dp[i][j],ma2); } } } for(int i=x;i<=n;++i) { for(int j=y;j<=m;++j) { if(i == x && j == y) continue; if(mp[i][j] < he[i][j]) continue; LL ma1,ma2; if(i-1 >= 1) { ma1 = dp[i-1][j]+mp[i][j]-he[i][j]; dp[i][j] = min(dp[i][j],ma1); } if(j-1 >= 1) { ma2 = dp[i][j-1]+mp[i][j]-he[i][j]; dp[i][j] = min(dp[i][j],ma2); } } } ans = min(ans,dp[1][1]+dp[n][m]); } int main() { int t;sd(t); while(t--) { ans = INF; sdd(n,m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) sld(mp[i][j]); for(int i=1;i<=n;++i) { for(int j=1;j<=m;++j) { slove(i,j); } } plr(ans); } system("pause"); return 0; }