Codeforces 429B B. Working out
题意:在 n * m ( 3 ≤ n, m ≤ 1000 ) 的网格中,A从(1,1)点移动到(n,m)点,每次只能向下或者向右移动一格,B从(n,1)点移动到(1,m)点,每次只能向上或者向右移动一格,他们的速度不同,走到一个格子后可以获得相应的权值,当相遇于同一个网格时,他们都不会拿该权值,问 A 与 B 可以拿到的权值之和的最大值。
分析:可以先dp出每个网格到四个角的最大权值,可以枚举每个网格为A与B相遇的格子,因为要使权值最大,所以要使他们相遇的格子尽量少,A与B通过网格的方式如下图,枚举转移之后即为答案。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<map>
#include<deque>
using namespace std;
typedef long long ll;
#define eps 1e-8
#define pi acos(-1.0)
template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F&&(num=-num);
}
const int maxn=1010;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
inline ll mul(ll a,ll b){
ll c=a*b-(ll)((long double)a*b/mod+0.5)*mod;
return c<0?c+mod:c;
}
//void print(__int128 x){
// if(x<0){
// putchar('-');
// x = -x;
// }
// if(x>9) print(x/10);
// putchar(x%10+'0');
//}
ll dp[5][maxn][maxn];
ll a[maxn][maxn];
int main(){
int n,m;
read(n);
read(m);
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
read(a[i][j]);
}
}
//处理出到四角的最大值
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
dp[1][i][j]=max(dp[1][i][j-1],dp[1][i-1][j])+a[i][j];
}
for(int j=m;j>0;j--){
dp[2][i][j]=max(dp[2][i][j+1],dp[2][i-1][j])+a[i][j];
}
}
for(int i=n;i>0;i--){
for(int j=1;j<=m;j++){
dp[3][i][j]=max(dp[3][i][j-1],dp[3][i+1][j])+a[i][j];
}
for(int j=m;j>0;j--){
dp[4][i][j]=max(dp[4][i][j+1],dp[4][i+1][j])+a[i][j];
}
}
//初始化边界
for(int i=0;i<=n+1;i++){
for(int j=1;j<=4;j++){
dp[j][i][0]=dp[j][i][m+1]=-inf;
}
}
for(int i=0;i<=m+1;i++){
for(int j=1;j<=4;j++){
dp[j][0][i]=dp[j][n+1][i]=-inf;
}
}
ll ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
ll cnt=0;
cnt=dp[1][i-1][j]+dp[4][i+1][j]+dp[3][i][j-1]+dp[2][i][j+1];//出入枚举网格的两种方式
ans=max(ans,cnt);
cnt=dp[2][i-1][j]+dp[3][i+1][j]+dp[1][i][j-1]+dp[4][i][j+1];
ans=max(ans,cnt);
}
}
printf("%lld\n",ans);
}
UVA 10328 Coin Toss
题意:现有 n 张牌,求出至少有 k 张牌连续位于正面的种数。
分析:当问 ” 至多连续 “ 的数量时,就是典型的dp问题,所以可以简单转换一下,问题可以转换成总数量- 至多有k个连续相同种类数量。
dp[ i ][ j ] 可表示为前 i 张牌连续正面 j 次的数量。因为第 i 张牌有两种情况,所以 dp[ i ][ j ] = dp[ i - 1 ][ j ] * 2,但是把不符合情况的数量也计算进去了。
当 i > j + 1时可能会出现加上第 i 位后出现连续相同的 j + 1 个数的情况,投掷 i - 1 次,连续正面的个数不超过 j 个的话,又要说最后 j 个都为正面,那么第i - j - 1个就一定是反面(对应这类情况而言,并不是说 i - j - 1为反面就一定不符合),那么只要满足前 i - j - 2次中连续正面的个数不超过 j 的话,就是要减掉的那部分。
当 i= j + 1 时,只需要减去一种情况,即出现连续 j + 1 个正面出现。
因为数据过大,应该使用 java 高精度,但是猜测数据没超过 2128,所以使用 __int128 水过去了。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<string>
#include<set>
#include<vector>
#include<queue>
#include<cmath>
#include<stack>
#include<map>
#include<deque>
using namespace std;
typedef long long ll;
#define eps 1e-8
#define pi acos(-1.0)
template<class T> void read(T&num) {
char CH; bool F=false;
for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
F&&(num=-num);
}
const int maxn=110;
const int inf=0x3f3f3f3f;
const ll mod=1e9+7;
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
inline ll mul(ll a,ll b){
ll c=a*b-(ll)((long double)a*b/mod+0.5)*mod;
return c<0?c+mod:c;
}
void print(__int128 x){
if(x<0){
putchar('-');
x = -x;
}
if(x>9) print(x/10);
putchar(x%10+'0');
}
__int128 dp[maxn][maxn];
int main(){
for(int i=0;i<=100;i++){
dp[0][i]=1;
dp[i][0]=1;
dp[1][i]=2;
}
for (int i = 2 ; i <= 100 ; i++){
for (int j = 1 ; j <= 100 ; j++){
dp[i][j]=dp[i-1][j]*2;
if(i==j+1)
dp[i][j]=dp[i][j]-1;
else if(i>j+1)
dp[i][j]=dp[i][j]-dp[i-j-2][j];
}
}
int n,k;
while(~scanf("%d %d",&n,&k)){
print(dp[n][n]-dp[n][k-1]);
puts("");
}
}