《HDU多校第四场》

Equal Sentences

思路:因为下标的限制,交换后位置不能和原来超过1。

那么,和后面交换,可以看成后面的和前面的交换。

那么对于每一个位置,只需要看和前面交换即可。

对于没有冲的ABCD类的位置交换组合。

经过递推之后,可以发现就是个斐波那契数列。

当引入了重复后,从只和前面的交换来看。

用dp[i]来表示到i的方案数

首先加入i点,方案为dp[i-1].

如果和前面不重复,那么交换后会形成新的方案,即dp[i-2]

所以转移方程即为dp[i] = dp[i-1] + (s[i] == s[i-1] ? 0 : dp[i-2])

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<int,int> pii;
const int N = 2e5+5;
const int M = 2e5+5;
const LL Mod = 1e9+7;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
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;
}
LL dp[N];
string t[N];
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n;n = read();
        string s;getline(cin,s);
        memset(dp,0,sizeof(dp));
        int len = s.size(),k = 0;
        string tt = "";
        for(int i = 0;i < len;++i)
        {
            if(s[i] == ' ')
            {
                t[++k] = tt;
                tt = "";
            }
            else tt += s[i];
        }
        t[++k] = tt;
        dp[1] = 1,dp[0] = 1;
        for(int i = 2;i <= n;++i)
        {
            dp[i] = dp[i-1];
            if(t[i] != t[i-1]) dp[i] = (dp[i]+dp[i-2])%Mod;
        }
        printf("%lld\n",dp[n]);
    }
    //system("pause");
    return 0;
}
View Code

Deliver the Cake

思路:一开始只是在最短路上进行模改,然后直接跑。

一直TLE。因为可能会多次去改变一个点的最优状态,导致了时间复杂度飙升。

正解:对于L,R这些点,很显然在这个城市上,就应该是哪只手,所以需要考虑的只是在M城市上应该是哪只手。

又因为在M城市上显然只有两种可能,那么进行拆点,对于M城市建立两个点,一个是左手点,一个是右手点。

然后对相邻的点连边,如果不同就加上代价x。

需要注意的是,s和t也可能拆过,所以最优解应该是在min(dis{s1,s2} - {t1,t2})

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 2e5+5;
const LL Mod = 1e9+7;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL 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 n,m,s,t,col[N<<1],head[N<<1],cnt = 0;
LL x,dis[N<<1];
char ss[N];
struct Node{int to,next;LL dis;}e[M*8];
inline void add(int u,int v,LL w)
{
    e[++cnt].to = v,e[cnt].dis = w,e[cnt].next = head[u],head[u] = cnt;
    e[++cnt].to = u,e[cnt].dis = w,e[cnt].next = head[v],head[v] = cnt;
}
void dij(int st)
{
    for(int i = 1;i <= n*2;++i) dis[i] = INF;
    dis[st] = 0;
    priority_queue<pii,vector<pii>,greater<pii> > Q;
    Q.push(pii{0,st});
    while(!Q.empty())
    {
        int u = Q.top().second;
        LL d = Q.top().first;
        Q.pop();
        if(d > dis[u]) continue;
        for(int i = head[u];i;i = e[i].next)
        {
            int v = e[i].to;LL dd = e[i].dis;
            if(dis[v] > dis[u]+dd)
            {
                dis[v] = dis[u]+dd;
                Q.push(pii{dis[v],v});
            }
        }
    }
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        n = read(),m = read(),s = read(),t = read(),x = read();
        cnt = 0;
        memset(head,0,sizeof(head));
        scanf("%s",ss);
        int len = strlen(ss);
        for(rg int i = 0;i < len;++i) 
        {
            if(ss[i] == 'L') col[i+1] = -1;
            else if(ss[i] == 'R') col[i+1] = 1;
            else col[i+1] = 0;
        }
        while(m--)
        {
            int u,v;
            LL w;//小 - 左,大 - 右
            u = read(),v = read(),w = read();
            if(col[u] != 0 && col[v] == 0) swap(u,v);
            if(col[u] == 0) 
            {
                if(col[v] == 0)
                {
                    add(u,v,w);//左 - 左
                    add(u+n,v+n,w);//右 - 右
                    add(u,v+n,w+x);//左 - 右
                    add(u+n,v,w+x);//右 - 左
                }
                else if(col[v] == -1)
                {
                    add(u,v,w);//左 - 左
                    add(u+n,v,w+x);//右 - 左
                }
                else
                {
                    add(u,v,w+x);//左 - 右
                    add(u+n,v,w);//右 - 右
                }
            }
            else 
            {
                int ma = (col[u] == col[v] ? 0 : x);
                add(u,v,w+ma);
            }
        }
        LL ans = INF;
        dij(s);
        ans = min(ans,min(dis[t],dis[t+n]));
        if(col[s] == 0) 
        {
            dij(s+n);
            ans = min(ans,min(dis[t],dis[t+n]));
        }
        printf("%lld\n",ans);
    }
    system("pause");
    return 0;
}
/*
1 
3 3 1 3 20
MRM
1 2 10
2 3 10
1 3 100
ans - 20
*/
View Code

Go Running

思路:从坐标轴出发。x为纵坐标,t为横坐标

因为每个人每秒钟只走1的单位距离。

那么可以发现对于每个人的运动坐标,都是k = 1或-1的直线(= 1向东走,-1向西走)

这坐标轴上可见,每个人都有两条直线延伸到的可能。每个人都会被一条直线覆盖到。

当一条直线覆盖到多个人时,这条直线上的人都可能是一个人跑的。

那么显然要最小化选到的直线,让这些直线覆盖到所有点。

如果让点和直线连边,会发现难以实现。因为一个人会连正值k线和负值k线,变得很困难。

那么如果让正值k线和负值k线连边,可以发现就是一个二分图的模型。

我们要选择较少的正k值k线,覆盖了所有的负值k线。那么就是个最小点覆盖的问题。

匈牙利会超时,直接上HK

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e5+5;
const int M = 2e5+5;
const LL Mod = 1e9+7;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL 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 match[N],tag[N],vis[N],n,cnt1,cnt2;
int dx[N],dy[N];
vector<int> G[N];
bool bfs()
{
    memset(dx,0,sizeof(dx));
    memset(dy,0,sizeof(dy));
    bool flag = false;
    queue<int> Q;
    for(int i = 1;i <= cnt1;++i) if(tag[i] == -1) Q.push(i);
    while(!Q.empty())
    {
        int u = Q.front();
        Q.pop();
        for(auto v : G[u])
        {
            if(dy[v] != 0) continue;
            dy[v] = dx[u]+1;
            if(match[v] == -1) flag = true;
            else
            {
                dx[match[v]] = dy[v]+1;
                Q.push(match[v]);
            }
        }
    }
    return flag;
}
bool Find(int x)
{
    for(auto v : G[x])
    {
        if(vis[v] || dy[v] != dx[x]+1) continue;
        vis[v] = 1;
        if(match[v] == -1 || Find(match[v]))
        {
            match[v] = x;
            tag[x] = v;
            return true;
        }
    }
    return false;
}
int HK()
{
    memset(tag,-1,sizeof(tag));
    memset(match,-1,sizeof(match));
    int ans = 0;
    while(bfs())
    {
        memset(vis,0,sizeof(vis));
        for(int i = 1;i <= cnt1;++i) if(tag[i] == -1 && Find(i)) ans++;
    }
    return ans;
}
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        n = read();
        unordered_map<int,int> mp1,mp2;
        cnt1 = 0,cnt2 = 0;
        for(int i = 1;i <= n;++i) G[i].clear();
        for(rg int i = 1;i <= n;++i)
        {
            int t,x;t = read(),x = read();
            if(mp1[x+t] == 0) mp1[x+t] = ++cnt1;
            if(mp2[x-t] == 0) mp2[x-t] = ++cnt2;
            G[mp1[x+t]].push_back(mp2[x-t]);
        }
        printf("%d\n",HK());
    }
    system("pause");
    return 0;
}
View Code

Contest of Rope Pulling

1:歪解:

对两组的人进行01背包。

因为要人数相等才能减到1,所以上界只需要两组人总和较小的。

然后从0开始动态维护上界。最坏复杂度5e8左右。

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef long double ld;
typedef pair<LL,int> pii;
const int N = 1e3+5;
const int M = 1e6+5;
const LL Mod = 1e9+7;
#define rg register
#define pi acos(-1)
#define INF 1e18
#define INM INT_MIN
#define dbg(ax) cout << "now this num is " << ax << endl;
inline LL read()
{
    LL 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;
}
LL w1[1005],v1[1005],w2[1005],v2[1005];
LL dp1[M],dp2[M];
int main()
{
    int ca;ca = read();
    while(ca--)
    {
        int n,m;n = read(),m = read();
        int ma1 = 0,ma2 = 0;
        for(int i = 1;i <= n;++i)
        {
            w1[i] = read(),v1[i] = read();
            ma1 += w1[i];
        }
        for(int i = 1;i <= m;++i)
        {
            w2[i] = read(),v2[i] = read();
            ma2 += w2[i];
        }
        int up = min(ma1,ma2);
        for(int i = 0;i <= up;++i) dp1[i] = dp2[i] = -INF;
        dp1[0] = dp2[0] = 0;
        int tmp1 = 0,tmp2 = 0;
        for(int i = 1;i <= n;++i)
        {
            tmp1 += w1[i];
            for(int j = min(tmp1,up);j >= w1[i];--j) dp1[j] = max(dp1[j],dp1[j-w1[i]]+v1[i]);
        }
        for(int i = 1;i <= m;++i)
        {
            tmp2 += w2[i];
            for(int j = min(tmp2,up);j >= w2[i];--j) dp2[j] = max(dp2[j],dp2[j-w2[i]]+v2[i]);
        }
        LL ans = -INF;
        for(int i = 0;i <= up;++i) ans = max(ans,dp1[i]+dp2[i]);
        printf("%lld\n",ans);
    }
    system("pause");
    return 0;
}
View Code

2:正解:

那么,考虑到两组人要w的权值相同。

那么我们可以对第二组人的w取反。

那么第一组人的w值肯定为正,第二组人的w值肯定为负。

那么题目就变成了,找出部分人,w的总和相加为0时,v的最大总值。

我们随机化所有人的位置。来保证w的前缀和一直在0左右浮动,不让这个值过大。

然后就可以进行01背包,以某个数据为中值(这里为40000)作为0的替代点,进行dp

但是这里有点看脸,反正随机了蛮多次都拉胯了

View Code
原文地址:https://www.cnblogs.com/zwjzwj/p/13407814.html