2011icpc上海(重温经典)

2011icpc上海(重温经典)

导语

涉及的知识点

思维,哈希,搜索,数学,几何,DP

链接:上海站

题目

A

题目大意:给一个背包,有两种物品, 每种物品给出重量和价值,物品的个数是无穷的,求出能得到的最大价值,背包的容量最大可达 2 31 2^{31} 231

思路:咋一看是完全背包,但其实不是,因为背包的容量过大,所以不能用单纯的完全背包的思路来处理,但是单纯的按照性价比贪心,所得到的值也并不是最优的,例如全放性价比低刚好填满这种
由于最优解由s2和s1构成,暴力的来想,可以直接枚举某一种物品的取值个数,然后求出每种取值个数下的值最后取最值,现在的问题转换成了选择哪一种物品,如果选择性价比低的物品(假设是s2),那么该物品的个数范围为min(n/s2,s1-1),原因很简单,如果物品的个数到达了s1,那么由v1/s1=v2/s2,s2个性价比低物品可以用s1个更高的来换,至于n/s2是能选取的个数

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t;
void solve(int index) {
    ll n,s1,v1,s2,v2,ans=0;
    cin >>n>>s1>>v1>>s2>>v2;
    if(v1*s2<v2*s1) {//默认交换为s2
        swap(v1,v2);
        swap(s1,s2);
    }
    for(int i=0; i<=min(n/s2,s1-1); i++)//直接枚举
        ans=max(ans,i*v2+(n-i*s2)/s1*v1);
    cout <<ans<<endl;
}
int main() {
    ios::sync_with_stdio(0);
    cin >>t;
    for(int i=1; i<=t; i++) {
        cout <<"Case #"<<i<<": ";
        solve(i);
    }
    return 0;
}

思路2:假设1的性价比高于2.首先通过观察可以得到一个结论,即2的个数不会超过一个值。考虑这样一个情况,
由若干个1(假设为N1)消耗的体积与若干个2(假设为N2)消耗的体积是一致的,那么2的个数不会超过N2。每当
2的个数达到N2时我们总可以用N1个1来替换N2个2,这样总价值永远不会比之前的方案小。于是我们寻找1和2体积的
最小公倍数,挨个遍历一遍即可。

ps:感谢涛哥

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll gcd(ll long m,ll n) {
    ll temp;
    while(n!=0) {
        m%=n;
        temp=m;
        m=n;
        n=temp;
    }
    return m;
}
ll lcm(ll m,ll n) {
    ll gc=gcd(m,n);
    return m/gc*n;
}
int main() {
    int t;
    cin >>t;
    for(int l=1; l<=t; l++) {
        printf("Case #%d: ",l);
        ll n,s1,v1,s2,v2,q1,q2;
        cin>>n>>s1>>v1>>s2>>v2;
        ll sum=0;
        ll lc=lcm(s1,s2);
        if((double)v1/s1<(double)v2/s2) {
            swap(s1,s2);
            swap(v1,v2);
        }
        ll k=lc/s2,maxi=0;
        for(int i=0; i<k&&i*s2<=n; i++) {
            q1=i*v2;
            q2=(n-i*s2)/s1*v1;
            if(q1+q2>maxi) maxi=q1+q2;
        }
        sum=maxi;
        cout<<sum<<endl;
    }
    return 0;
}

F

题目大意:略

思路:将字符串哈希成编号,然后分成名词和动词分别处理,对于每次给出的句子进行构图和查询,即使是问句里出现的新词语也要插入,因为如果不插入就会被视为所有未在陈述中出现的词语都相等

ps:这份代码是学长修改后的,讨论之后觉得并没有问题,但还是没有过,可能是题目数据的问题

代码

#include <bits/stdc++.h>

#define int long long

using namespace std;

const int maxm = 1e5 + 10, maxn = 1e3;
int t, cas, head[maxn], cnt, id;
map<string, int>mpn, mpv;
string a, b, c, d;
bool vis[maxn];

struct node {
    int to, next;
} e[maxm];

void Add(int from, int to) { //链式前向星
    e[++cnt].next = head[from];
    e[cnt].to = to;
    head[from] = cnt;
}

bool DFS(int x, int y) {
    if(x == y || vis[y]) return 1; //如果是同一个点
    vis[x] = 1; //标记已访问
    for(int i = head[x]; i; i = e[i].next) {
        int v = e[i].to;
        if(vis[v])continue;
        if(DFS(v, y)) {return 1;}
    }
    return 0;
}

void ask(int type) {
    memset(vis, 0, sizeof(vis));
    bool flag = 0;
    switch(type) {
    case 0://名词和名词
        if(mpn[a] == 0)mpn[a] = ++id;
        if(mpn[b] == 0)mpn[b] = ++id;
        flag = DFS(mpn[a], mpn[b]);
        break;
    case 1://名词和动词
        if(mpn[a] == 0)mpn[a] = ++id;
        if(mpv[b] == 0)mpv[b] = ++id;
        flag = DFS(mpn[a], mpv[b]);
        break;
    case 2://动词和动词
        if(mpv[a] == 0)mpv[a] = ++id;
        if(mpv[b] == 0)mpv[b] = ++id;
        flag = DFS(mpv[a], mpv[b]);
        break;
    default://动词和名词
        if(mpv[a] == 0)mpv[a] = ++id;
        if(mpn[b] == 0)mpn[b] = ++id;
        flag = DFS(mpv[a], mpn[b]);
        break;
    }
    flag ? cout << "Y" : cout << "M";
}

void connect(int type) {
    switch(type) {
    case 0://名词和名词
        if(mpn[a] == 0)mpn[a] = ++id;
        if(mpn[b] == 0)mpn[b] = ++id;
        Add(mpn[a], mpn[b]);
        break;
    case 1://名词和动词
        if(mpn[a] == 0)mpn[a] = ++id;
        if(mpv[b] == 0)mpv[b] = ++id;
        Add(mpn[a], mpv[b]);
        break;
    case 2://动词和动词
        if(mpv[a] == 0)mpv[a] = ++id;
        if(mpv[b] == 0)mpv[b] = ++id;
        Add(mpv[a], mpv[b]);
        break;
    case 3://动词和名词
        if(mpv[a] == 0)mpv[a] = ++id;
        if(mpn[b] == 0)mpn[b] = ++id;
        Add(mpv[a], mpn[b]);
        break;
    }
}

signed main() {
    ios::sync_with_stdio(0);
    cin >> t;
    while(t--) {
        cout << "Case #" << ++cas << ":\n";
        mpn.clear();
        mpv.clear();
        id = cnt = 0;
        memset(head, 0, sizeof(head));
        while(cin >> a && a[a.length() - 1] != '!') {
            //memset(vis, 0, sizeof(vis)); //清空访问标记
            cin >> b >> c; //输入前三个
            int len = c.length();
            if(c[len - 1] == '?') { //判断长度是否为三,且为问号
                c.erase(c.end() - 1);
                swap(a, c);
                swap(a, b); //确保a,b的相对位置
                c == "are" ? ask(0) : ask(1); //询问
                continue;
            }
            else if(c[len - 1] == '.') { //如果是陈述句
                c.erase(c.end() - 1);
                swap(b, c);
                c == "are" ? connect(0) : connect(1);
                continue;
            }
            cin >> b >> c >> d;
            len = d.length();
            if(d[len - 1] == '?') { //长度为6的问句
                d.erase(d.end() - 1);
                swap(a, c);
                swap(b, d);
                d == "are" ? ask(3) : ask(2);
            }
            else {
                d.erase(d.end() - 1);
                swap(b, d);
                swap(d, a);
                c == "are" ? connect(3) : connect(2);
            }
        }
        //if(t != 0)
            cout << endl;
    }
    return 0;
}

I

题目大意:略,翻译可见UVA12333 Fibonacci的复仇 Revenge of Fibonacci

思路:具体参考文末给出的洛谷的题解,感觉讲的比CSDN上的题解好很多

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=4586400;
int vis[maxn],t,a[2][52]= {{1},{1}},tot,inf,trie[maxn][10];
char s[100];
void Insert(int *a,int v) {
    int i=50;
    while(!a[i])i--;
    for(int p=0; i>=0; i--) {
        if(!trie[p][a[i]])trie[p][a[i]]=++tot;
        p=trie[p][a[i]];
        if(vis[p]==inf)vis[p]=v;//只需要记一次即可
    }
}
void Query(char *s) {
    int p=0;
    for(int i=0; s[i]; i++) {
        int ch=s[i]-'0';
        if(!trie[p][ch]) {
            cout <<-1<<endl;
            return;
        }
        p=trie[p][ch];
    }
    cout <<vis[p]<<endl;
}
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    memset(vis,0x3f,sizeof(vis));
    inf=vis[0];
    Insert(a[0],0);//这里需要插入,作为根节点
    for(int i=2; i<100000; i++) {
        if(a[1][50]||a[0][50])//如果位对不上
            for(int j=0; j<=50; j++) {//复制
                a[0][j]=a[0][j+1];
                a[1][j]=a[1][j+1];
            }
        for(int j=0; j<50&&a[j]; j++) {//开始模拟运算
            a[i&1][j]+=a[(i&1)^1][j];
            a[i&1][j+1]+=a[i&1][j]>=10;
            a[i&1][j]%=10;
        }
        Insert(a[i&1],i);
    }
    cin >>t;
    for(int i=1; i<=t; i++) {
        cin >>s;
        cout <<"Case #"<<i<<": ";
        Query(s);
    }
    return 0;
}

参考文献

  1. UVa12325 12325 - Zombie‘s Treasure Chest(思路+代码)
  2. HDU 4096 Universal Question Answering System(输入用gets或getchar)
  3. HDU 4099 Revenge of Fibonacci(高精度加法+字典树Trie)
  4. HDU4099(斐波那契数列与字典树)
  5. UVA12333 Fibonacci的复仇 Revenge of Fibonacci 题解
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值