TopCoder SRM623 DIV1

博客讨论了TopCoder SRM623 DIV1比赛中的300分和450分问题,涉及动态规划(DP)和最长递增子序列(LIS)。通过变换坐标解决接球问题,将原问题转化为求解LIS的DP问题。文章强调了题目的经典转化和LIS算法在解决此类问题中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

300

预处理,这道题目和DIV2的最后一题类似,是那道题目的简化版。

// BEGIN CUT HERE

// END CUT HERE
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctime>
#include <utility>
#include <iterator>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef set<int> si;
typedef map<int,int> mii;
typedef map<string,int> msi;
typedef pair<int,int> pii;
typedef priority_queue<int,vector<int>,greater<int> > pless;
typedef priority_queue<int> pgreater;

#define clr(x,a) memset(x,a,sizeof(x))
#define sz(x) (int)x.size()
#define pb push_back
#define mp make_pair

#define REP(i,n) for(i=0;i<(n);++i)
#define FOR(i,l,h) for(i=(l);i<=(h);++i)
#define FORD(i,h,l) for(i=(h);i>=(l);--i)



class UniformBoard
{
        public:
        int sa[55][55];
        int sp[55][55];
        int se[55][55];
        int getBoard(vector <string> board, int K)
        {
            int i,j,k;
            int n=sz(board);
            clr(sa,0);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++){
                    int ret=0;
                    if(board[i-1][j-1]=='A') ret=1;
                    sa[i][j]=sa[i-1][j]+sa[i][j-1]-sa[i-1][j-1]+ret;
                }
            clr(sp,0);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++){
                    int ret=0;
                    if(board[i-1][j-1]=='P') ret=1;
                    sp[i][j]=sp[i-1][j]+sp[i][j-1]-sp[i-1][j-1]+ret;
                }
            clr(se,0);
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++){
                    int ret=0;
                    if(board[i-1][j-1]=='.') ret=1;
                    se[i][j]=se[i-1][j]+se[i][j-1]-se[i-1][j-1]+ret;
                }
            int res=0;
            for(int i=1;i<=n;i++)
                for(int j=1;j<=n;j++)
                    for(int x=i;x<=n;x++)
                        for(int y=j;y<=n;y++){
                            int l=y-j+1;
                            int w=x-i+1;
                            if(l*w>sa[n][n]) continue;
                            int a=sa[x][y]-sa[x][j-1]-sa[i-1][y]+sa[i-1][j-1];
                            int p=sp[x][y]-sp[x][j-1]-sp[i-1][y]+sp[i-1][j-1];
                            int e=se[x][y]-se[x][j-1]-se[i-1][y]+se[i-1][j-1];
                            int step=0;
                            if(e!=0) step+=e;
                            if(p!=0&&se[n][n]!=0) step+=2*p;
                            if(p!=0&&se[n][n]==0) continue;
                            if(step<=K)
                                res=max(res,l*w);
                        }
            return res;
        }

// BEGIN CUT HERE
	
// END CUT HERE

};
// BEGIN CUT HERE



450

DP ,LIS

和有意思的一道题目,题目的背景和DIV2的第一题相同,不同之处,是DIV1让我们判断能不能接到所有的坠落物,而这道题目则是问我们按照规则移动的条件下最多能够街道多少个物体。

我们社前后能够接到的球 分别为(x0,y0) (x1,y1)

我们知道这两个球的落点距离差为 | x1-x0 |   ,同样这个差也代表了要消耗的单位时间。 然后我们可以用y坐标做差 y1-y0 这样我们就得到两个物体落地时间的差值,这个差值代表了从0到1最慢要花多少时间。

显然,   如果能够连续接到两个物体,那么 |x1-x0|<=y1-y0

去掉绝对值之后 y1-y0>=x1-x0  , y1-y0>=x0-x1

移项之后我们得到两个不等式  y1-x1>=y0-x0   , y1+x1>=y0+x0

对于所有坐标(x,y) 我们进行变换 设 s=y1-x1  d=y1+x1 得到 新的坐标(s,d)

然后对于新坐标的人一个变量排序再对另一个变量求LIS这个问题边转化成了一个DP问题。

这道题目处理LIS 需要用到LIS的nlogn算法,这个网上资料听得多的。

这道题目的转化很经典。

// BEGIN CUT HERE

// END CUT HERE
#include <cstdlib>
#include <cctype>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
#include <sstream>
#include <map>
#include <set>
#include <queue>
#include <stack>
#include <ctime>
#include <utility>
#include <iterator>

using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef set<int> si;
typedef map<int,int> mii;
typedef map<string,int> msi;
typedef pair<int,int> pii;
typedef priority_queue<int,vector<int>,greater<int> > pless;
typedef priority_queue<int> pgreater;

#define clr(x,a) memset(x,a,sizeof(x))
#define sz(x) (int)x.size()
#define pb push_back
#define mp make_pair

#define REP(i,n) for(i=0;i<(n);++i)
#define FOR(i,l,h) for(i=(l);i<=(h);++i)
#define FORD(i,h,l) for(i=(h);i>=(l);--i)



class CatchTheBeat
{
        public:
        int l[500100],len;
        int binSearch(int L,int R,int v){
            int m;
            while(L<=R){
                m=(L+R)>>1;
                if(l[m]<=v)
                    L=m+1;
                else
                    R=m-1;
            }
            //cout<<L<<endl;
            return L;
        }
        int maxCatched(int n, int x0, int y0, int a, int b, int c, int d, int mod1, int mod2, int offset)
        {
            //题目里面有句话
            //Watch out for integer overflow when generating the coordinates.
            vector<ll> x(n),y(n);
            x[0]=(ll)x0;
            for(int i=1;i<n;i++)
                x[i]=(x[i-1]*a+b)%(ll)mod1;
            for(int i=0;i<n;i++)
                x[i]=x[i]-offset;
            y[0]=y0;
            for(int i=1;i<n;i++)
                y[i]=(y[i-1]*c+d)%(ll)mod2;
            vector<pii> vp;
          
            for(int i=0;i<n;i++)
                if(y[i]>=0&&y[i]-x[i]>=0&&y[i]+x[i]>=0)  //初始的点没加进去(0,0)
                    vp.pb(mp(y[i]-x[i],y[i]+x[i]));
            sort(vp.begin(),vp.end());
            //LIS
            len=0,l[0]=0;
            n=sz(vp);
            for(int i=0;i<n;i++){
                int pos=binSearch(0,len,vp[i].second);
                if(pos>len){
                    l[pos]=vp[i].second;
                    len++;
                }else if(pos!=0)
                    l[pos]=min(l[pos],vp[i].second);
            }
            return len;
        }

// BEGIN CUT HERE
	
// END CUT HERE

};
// BEGIN CUT HERE

// END CUT HERE

    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值