中大ACM个人赛 ABC题题解

摸完签到题就跑了

A - Maximum Subrectangle

题意:有个C矩阵,C[i][j] = a[i]b[j], 问你在(displaystylesum_{i=x1}^{x2}) (displaystylesum_{j=y1}^{y2}) (C_{i,j}) <= x的条件下能选出来最大子矩阵。

思路:不妨设这个子矩阵的左上角为(i,j), 右下角为(i+len1-1,j+len2-1),其中len1,len2分别代表长和宽(或宽和长)。
这个时候可以把元素和写成(a[i] + a[i+1] ... + a[i+len1-1])(b[j] + b[j+1] + ... b[j+len2-1]),这可以看成在a数组中挑出一个长度为len1的子串,在b数组中挑出一个长度为len2的子串,两者的子串和相乘。那么我们要想选出总共那么多个数的时候,各自的和尽量的小(贪心),这样才能使得相乘起来更小。比如这里的len1长度的a子串和len2长度的b子串,我要挑肯定挑各自那么长的子串中最小的。
所以问题就变成了我要在各自的数组里面先计算好长度为len时的最优解,然后遍历各自取出的长度即可。时间复杂度O((n^2))。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 2e3+200;
const ll inf=0x3f3f3f3f3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll n, m;
ll a[maxn], b[maxn];
ll pre1[maxn], pre2[maxn];  //pre[i]代表子串长度为i时的最小和
ll sum1[maxn],sum2[maxn];

int main()
{
    n = read(), m = read();
    rep(i,1,n) a[i] = read(), sum1[i] = a[i] + sum1[i-1];
    rep(i,1,m) b[i] = read(), sum2[i] = b[i] + sum2[i-1];
    ll x = read();
    rep(len,1,n)
    {
        ll mi = inf;
        rep(j,len,n) mi = min(mi, sum1[j] - sum1[j-len]);
        pre1[len] = mi;
    }
     rep(len,1,m)
    {
        ll mi = inf;
        rep(j,len,m) mi = min(mi, sum2[j] - sum2[j-len]);
        pre2[len] = mi;
    }
    ll ans = 0;
    rep(i,1,n) rep(j,1,m)
    {
        if(pre1[i]*pre2[j] <= x) ans = max(ans, i*j);
    }
    cout<<ans<<'
';
    return 0;
}



B - Barcelonian Distance

题意:给你两个点,每步只能按照曼哈顿路径走,但是也给了你一条直线,如果在直线上就能沿着直线滑行。问你从A到B的最短路。

思路:其实不用想那么多,建边跑最短路floyd就行。一共有六个点:
1.A点。
2.A横着到直线相交的那个点。
3.A竖着到直线相交的点。
4.B点。
5.B横着交直线的点。
6.B竖着交直线的点。
这几个点先求出来然后建边,跑个floyd直接输出就好。
另外一个方法就是枚举这几种路径,就是麻烦了一点,效果一样:
1.A直接曼哈顿距离到B。
2.A沿着直线上,有两种方法,然后从直线到B,也有两种方法。
以上五种方法全写出来取最小就行。不过还是直接floyd香。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

double dp[20][20];
double a, b, c;
double x1, y1 , x2, y2;
typedef struct Pos
{
    double x;
    double y;
}P;
P point[10];

int main()
{
    cin>>a>>b>>c;
    cin>>x1>>y1>>x2>>y2;
    rep(i,1,10) rep(j,1,10) if(i!=j) dp[i][j] = 1e18;
    dp[1][6] = dp[6][1] = fabs(x1 - x2) + fabs(y1 - y2);
    if(a!=0&&b!=0)
    {
        point[1].x = x1;
        point[1].y = y1;

        point[2].x = x1;
        point[2].y = -(a*x1 + c)/b;

        point[3].x = -(b*y1+c)/a;
        point[3].y = y1;

        point[4].x = x2;
        point[4].y = -(a*x2 + c)/b;

        point[5].x = -(b*y2+c)/a;
        point[5].y = y2;

        point[6].x = x2;
        point[6].y = y2;

        rep(i,2,5) dp[1][i] = dp[i][1] = fabs(x1 - point[i].x) + fabs(y1 - point[i].y),dp[6][i] = dp[i][6] = fabs(x2 - point[i].x) + fabs(y2 - point[i].y);
        rep(i,2,5) rep(j,i+1,5) dp[i][j] = dp[j][i] = sqrt((point[i].x - point[j].x)*(point[i].x - point[j].x) +(point[i].y - point[j].y)*(point[i].y - point[j].y)  );
    }
    rep(k,1,6) rep(i,1,6) rep(j,1,6) dp[i][j] = min(dp[i][j], dp[i][k] + dp[k][j]);
    printf("%f
", dp[1][6]);
    return 0;
}


C - The Unbearable Lightness of Weights

题意:找到最大的k,使得有k个相同的数组成的和m,是被唯一得到的。

思路:其实根本不太懂题目问什么。看别人博客的题意来写的。
要满足上述限制还要最优,我们的策略就是尽量挑大的k,然后我们要check一下挑k个a[i]后组成的sum是否唯一得到。
所以事先要做一遍背包,以dp[i][j]表示用j个物品凑到i重量的方案数。转移时外层用1->n个砝码去铺,内层做一个背包。转移方程为:
dp[j][k] += dp[j-a[i]][k]。
这个预处理完后就是上面说的check,我挑k个a[i],看看dp[k*a[i]][k]是否等于(C_{Map[a[i]]}^{k}),其中Map[a[i]]为a[i]的个数。简单排列组合问题。
最后有点疑惑的是不知道为啥种类为2的时候也能得到所有,可能还是不太理解题意吧。

view code
#include<iostream>
#include<string>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include <queue>
#include<sstream>
#include <stack>
#include <set>
#include <bitset>
#include<vector>
#define FAST ios::sync_with_stdio(false)
#define abs(a) ((a)>=0?(a):-(a))
#define sz(x) ((int)(x).size())
#define all(x) (x).begin(),(x).end()
#define mem(a,b) memset(a,b,sizeof(a))
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define rep(i,a,n) for(int i=a;i<=n;++i)
#define per(i,n,a) for(int i=n;i>=a;--i)
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace std;
typedef long long ll;
typedef pair<ll,ll> PII;
const int maxn = 1e5+200;
const int inf=0x3f3f3f3f;
const double eps = 1e-7;
const double pi=acos(-1.0);
const int mod = 1e9+7;
inline int lowbit(int x){return x&(-x);}
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
void ex_gcd(ll a,ll b,ll &d,ll &x,ll &y){if(!b){d=a,x=1,y=0;}else{ex_gcd(b,a%b,d,y,x);y-=x*(a/b);}}//x=(x%(b/d)+(b/d))%(b/d);
inline ll qpow(ll a,ll b,ll MOD=mod){ll res=1;a%=MOD;while(b>0){if(b&1)res=res*a%MOD;a=a*a%MOD;b>>=1;}return res;}
inline ll inv(ll x,ll p){return qpow(x,p-2,p);}
inline ll Jos(ll n,ll k,ll s=1){ll res=0;rep(i,1,n+1) res=(res+k)%i;return (res+s)%n;}
inline ll read(){ ll f = 1; ll x = 0;char ch = getchar();while(ch>'9'||ch<'0') {if(ch=='-') f=-1; ch = getchar();}while(ch>='0'&&ch<='9') x = (x<<3) + (x<<1) + ch - '0',  ch = getchar();return x*f; }
int dir[4][2] = { {1,0}, {-1,0},{0,1},{0,-1} };

ll n;
ll dp[10005][105];    //dp[i][j]表示用j件物品凑到i重量的方案数
ll C[105][105];
ll a[maxn];
map<ll,ll> Map;

void init()
{
    rep(i,0, 102) C[i][0] = 1;
    rep(i,1,102) rep(j,1,i) C[i][j] = (C[i-1][j]+C[i-1][j-1])%mod;
}

int main()
{
    init();
    n = read();
    ll sum = 0;
    rep(i,1,n) a[i] = read(), sum += a[i], Map[a[i]]++;
    dp[0][0] = 1;
    rep(i,1,n) per(j,sum,a[i]) rep(k,1,i) dp[j][k] = (dp[j][k] + dp[j - a[i]][k-1]) % mod;
    if(Map.size()<=2)
    {
        cout<<n<<'
';
        return 0;
    }
    ll ans = 0;
    for(auto it = Map.begin(); it != Map.end(); it++)
    {
        ll cur = it->fi;
        rep(i,1,it->se) if(dp[cur*i][i] == C[it->se][i]) ans = max(ans,i);
    }
    cout<<ans<<'
';
    return 0;
}

原文地址:https://www.cnblogs.com/Bgwithcode/p/13619674.html