[XDFZ集训Day3]CSP模拟

[XDFZ集训Day3]CSP模拟

T1----ZYB建围墙

没用的找规律题,代码和题面直接略了

T2-----ZYB和售货机

可爱的ZYB来到一个售货机前。
售货机里有一共有𝑁(≤ 10^5) 个物品,每个物品有𝐴𝑖 个。自然,还有𝑁
个购买按钮。正常情况下,按下第𝑖 个按钮,需要支付𝐶𝑖 的钱,然后会跳
出一份物品𝑖。如果该物品卖完了,按下此按钮无效。
但是,这台售货机的电路连接出了点问题。第𝑖 个按钮的“弹出电路”
连向了物品𝑓𝑖。
假设按下了第𝑖 个按钮,售货机会按以下逻辑执行:
1. 判断第𝑖 个物品是否为空。
2. 如果是,不执行任何操作,退出该购买程序。
3. 否则,要求支付𝐶𝑖 的钱。
4. 因为电路坏了,实际弹出的物品会是𝑓𝑖。
注意:如果物品𝑓𝑖 为空,显然也不会有物品弹出。
ZYB很快发现了售货机的秘密,并精确掌握了𝑓𝑖 的值。他又去调查了
每一种物品的市场价。即他可以以𝐷𝑖 的价格卖掉物品𝑖。
现在ZYB他想通过这台售货机,赚尽量多的钱。
假设ZYB有足够多的成本钱

显然可以把 fi 和 i 连一条边,可以发现连出来的边一定是个基环树森林

每次都贪心取最优的边

考虑没有环的情况,即为一棵树,则对于一个节点只要他的儿子节点还没取完或他还没取完就可以继续取,所以从上向下取一定可以把所有节点取完

考虑有环的情况,显然在环的情况下会有后效性,把后效性消掉可以考虑在环上去掉一个换成第二优的边,此时一定最优

代码:

#include<bits/stdc++.h>
 
using namespace std;
 
inline int read()
{
    int f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch<'0'||ch>'9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch>='0'&&ch<='9');
    return f*x;
}
 
const int MAXN = 100000 + 10;
 
int n;
int f[MAXN],c[MAXN],d[MAXN],a[MAXN];
int val[MAXN];
int t1[MAXN],t2[MAXN];
int used[MAXN];
long long ans;
int tot;
int res;
 
inline void dfs(int x)
{
//  cout << x << " " << res << " " << used[x]<< endl;
    if(used[x] == tot)
    {
        ans -= res;
        return;
    }
    if(used[x]) return;
    res = min(res,val[t1[x]] - val[t2[x]]);
    used[x] = tot;
    if(t1[x]!=x) dfs(t1[x]);
    return;
}
 
int main()
{
    n = read();
    for(int i=1;i<=n;i++)
    {
        f[i] = read(),c[i] = read(),d[i] = read(),a[i] = read();
    }
    for(int i=1;i<=n;i++)
    {
        val[i] = d[f[i]] - c[i];
        if(val[i] < 0) continue;
        if(val[i] > val[t1[f[i]]]) t2[f[i]] = t1[f[i]],t1[f[i]] = i;
        else if(val[i] > val[t2[f[i]]]) t2[f[i]]= i;
    }
    for(int i=1;i<=n;i++) ans +=1LL *  val[t1[i]] * a[i];
    //cout << ans << endl;
    tot = 0;
    memset(used,0,sizeof(used));
    for(int i=1;i<=n;i++)
    {
        if(!used[i])
        {   
            tot++;
            res = 1<<30;
            dfs(i);
        }
    }
    cout << ans << endl;
}

T3 ZYB玩字符串

ZYB获得了一个神秘的非空字符串𝑝。
初始时,串𝑆是空的。
ZYB会执行若干次这样的操作:
1. 选取𝑆中的一个任意的位置(可以是最前面或者最后面)
2. 在这个位置上插入一个完整的𝑝,得到一个新的𝑆。
但是ZYB不小心把𝑝弄丢了。
他告诉你现在的𝑆是什么,请帮他还原出可能的𝑝。
如果有多个𝑝符合要求,选取长度最短的。
如果仍然有多解,选取字典序最小的。

神仙题,做不来

枚举字串,将问题转化为判定性问题

考虑用DP判定:

dp[l][r]表示对于枚举的字串是否成立

有两种转移的做法:

1.右边是缺的 dp[l][r] = dp[l][r-1](当前枚举位置匹配)

2.右边是完整的一截,即dp[l][r] = dp[l][k] & dp[k][r]

记忆化搜索实现

#include<bits/stdc++.h>

using namespace std;
 
#define ll long long
#define re register
 
inline ll read()
{
    ll f = 1,x = 0;
    char ch;
    do
    {
        ch = getchar();
        if(ch == '-') f = -1;
    }while(ch<'0'||ch>'9');
    do
    {
        x = (x<<3) + (x<<1) + ch - '0';
        ch = getchar();
    }while(ch>='0'&&ch<='9');
    return f*x;
}

const int MAXN = 200 + 10;

int T;
string s,sub,res;
int len;
int dp[MAXN][MAXN],cur;

inline bool solve(int l,int r)
{
//    cout << l << " " << r << endl;
    if(l>r) return 1;
    if(dp[l][r]!=-1) return dp[l][r];
    for(int i = r-cur;i>=l;i-=cur)
    {
        if(solve(l,i)&&solve(i+1,r))
        {
            dp[l][r] = 1;
            return dp[l][r];
        }
    }
    if(s[r-1]==sub[(r-l)%cur]&&solve(l,r-1)) return dp[l][r]=1;
    return dp[l][r]=0;
}

int main()
{
    T = read();
    while(T--)
    {
        bool flag = 0;
        cin >> s;
        len = s.length();
        for(cur=1;cur<=len;cur++)
        {
            if(len%cur==0)
            {
                for(int j=0;j<=len-cur;j++)
                {
                    sub = s.substr(j,cur);
                    //cout << sub << endl;
                    memset(dp,-1,sizeof(dp));
                    if(solve(1,len))
                    {
                        res = flag?min(res,sub):sub;flag = 1;
                    }
                }
            }
            if(flag) break;
        }
        cout << res << endl;
    }
}

 

 

posted @ 2019-12-12 19:50  Randyhoads  阅读(338)  评论(0)    收藏  举报