作业备份

A - The 3n + 1 problem

#include <iostream>
using namespace std;
//没说第一个数字大,但仍输出原来的第一位
int main()
{
    int n,m,i,j,cnt,max_cnt,ii,jj;
    while(cin >> i >> j){
        ii=i;
        jj=j;
        cnt=0;
        max_cnt=0;
        if(i>j){
            m=i;
            i=j;
            j=m;
        }
        for(m=i;m<=j;m++){
            cnt=1;//不会计算第一次
            n=m;//n,m必须分开,否则的数字一直会为1
            while (n!=1){
                cnt++;
                if(n%2==1) n=3*n+1;
                else n=n/2;
            }
            if(cnt>max_cnt)max_cnt=cnt;
        }
        cout << ii << " " << jj << " " <<max_cnt << endl;
    }
    return 0;
}

B - Candy Sharing Game

#include <iostream>
#include <string>
using namespace std;
int isequal(int *ls,int i)
{
    int flag=ls[0];
    for(int j=1;j<i;j++){
        if(ls[j]!=flag)
            return 0;
    }
    return 1;
}
int main()
{
    int n,j,i,cnt;
    int ls[100000];
    while(cin >> n){
        if(n==0)break;
        for(i=0;i<n;i++)
            cin >> ls[i];
        cnt=0;
        while(1){
            cnt++;
            //one turn
            int last=ls[n-1];//先记录最后一个
            for(j=i-1;j>0;j--){//应该从右往左算
                ls[j]=ls[j]/2+ls[j-1]/2;
            }
            ls[0]=ls[0]/2+last/2;
            //give one cake
            for(j=0;j<i;j++){
                if(ls[j]%2==1)
                    ls[j]++;
            }
            //is equal?
            if(isequal(ls,n))
                break;
        }
        cout << cnt << " " << ls[0] << endl;
    }
    return 0;
}

C - Edge(题目看不懂)

https://blog.csdn.net/thestarfish/article/details/46850619
题意:就是行走,然后输出走过的点,首先给出两个点(300,420)-> (310,420),然后A是顺时针,V是逆时针走,每次行走都是走10个单位,并且角度是90度,输出每个转弯点

#include <iostream>
#include <string>
using namespace std;
int main()
{
    int x=300,y=420;
    string dirs[4]={"right","down","left","up"};
    string dir=dirs[0];
    string s;
    while(cin >> s){
        cout << "300 420 moveto" << endl;
        cout << "310 420 lineto" << endl;
        int len=s.length();
        int i;
        int choice=0;
        char p;
        for(i=0;i<len;i++){
            p=s[i];
            if(p=='A')
                choice++;
            if(p=='V')
                choice+=3;
            dir=dirs[choice%4];
            if(dir=="right")
                x+=10;
            if(dir=="down")
                y-=10;
            if(dir=="left")
                x-=10;
            if(dir=="up")
                y+=10;
            cout << x << " " << y << " " << "lineto" << endl;
        }
        cout << "stroke" << endl;
        cout << "showpage" << endl;
    }
    return 0;
}

D - 不容易系列之(3)―― LELE的RPG难题 (recursive)

递推问题 越界 函数调用超时

https://blog.csdn.net/why850901938/article/details/50116937
n个方格的涂色方案可以由n - 1的涂色方案追加一个得出,分两种情况:
1.在n - 1的合法涂色方案后追加一个方格,由于合法方案的首尾颜色不同,因此第n个方格的颜色必定是这两种颜色之外的一种,即方案数为f[n - 1]。
2.在n - 1的不合法涂色方案(首尾颜色相同)后追加一个合法的涂色方格,也可能使其成为长度为n的合法涂色方案,而这种不合法涂色方案的结构必定是f[n - 2]合法方案 + 首格颜色 + 首格外的两种颜色,即方案数为2 * f[n - 2]。

//Time Limit Exceeded
#include<iostream>
using namespace std;
int f(int n)
{
    if(n==1)return 3;
    else if(n==2)return 6;
    else if(n==3)return 6;
    else return f(n-1)+2*f(n-2);
}
int main()
{
    int n;
    while(cin>>n)
        cout<<f(n)<<endl;
    return 0;
}
//Time Limit Exceeded

//Accepted
#include<iostream>
using namespace std;
//超过32 int越界
int main()
{
    long long n,ls[51],i;
    ls[1]=3;
    ls[2]=6;
    ls[3]=6;
    while(cin>>n){
        for(i=4;i<=n;i++)
            ls[i]=ls[i-1]+2*ls[i-2];
        cout<<ls[n]<<endl;
    }
    return 0;
}
//Accepted

E - A + B

#include <iostream>
#include <string>
using namespace std;
int trantonum(string a)
{
    int i;
    string ls[]={"zero","one","two","three","four","five","six","seven","eight","nine"};
    for(i=0;i<10;i++){
        if(a==ls[i])
            return i;
    }
    return 0;
}
int getnum(void)
{
    string a;
    int units=0,num=0;
    while(cin>>a){
        if(a=="="||a=="+")
            break;
        units=trantonum(a);
        num=units+num*10;
    }
    return num;
}
int main()
{
    string a;
    int num1,num2;
    while(1){
        num1=getnum();
        num2=getnum();
        if(num1==0&&num2==0)
            break;
        cout << num1+num2 << endl;
    }
    return 0;
}

F - Robot Motion

#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
#include <string>
using namespace std;
char grid[12][12];//创造一个12*12的矩阵,边界位置的值定为'0'
int main()
{
    int row,column,in,r,c,i;
    while(cin >> row >> column >> in){
        int X=in,Y=1;
        memset(grid,'0',sizeof(grid));
        if(row==0)break;
        string str;
        for(r=0;r<row;r++){
            cin >> str;
            for(c=1,i=0;c<=column;c++,i++)
                grid[r+1][c]=str[i];
        }
        vector<int>road;
        vector<int>::iterator p;
        int flag=X*10+Y;//记录走过的地方
        road.push_back(flag);
        int cnt=0;
        while(1){
            cnt++;
            char dir=grid[Y][X];
            if(dir=='N')Y-=1;
            else if(dir=='E')X+=1;
            else if(dir=='S')Y+=1;
            else if(dir=='W')X-=1;
            flag=X*10+Y;
            //判断以前是否走过以前的地方
            p = std::find(road.begin(),road.end(),flag);
            if(p!=road.end()){//FOUND!
                int pre=distance(road.begin(),p);
                int last=distance(p,road.end());
                cout<<pre<<" step(s) before a loop of "<<last<<" step(s)"<<endl;
                break;
            }
            //判断是否出去了
            if(grid[Y][X]=='0'){
                cout<<cnt<<" step(s) to exit"<<endl;
                break;
            }
            road.push_back(flag);
        }
    }
    return 0;
}

G - Football Game(结构体保存数据后sort)

题目看错很多次,先审题
字符串比较不用==,用strcpy,我也不知道为啥子

#include <cstring>
#include <cstdio>
#include <algorithm>
int num=-1;
using namespace std;
typedef struct info
{
    char name[100];
    int score,win,lose,net;
}info;
int in(char names[][100],char *name)
{
    int i;
    for(i=0;i<=num;i++){
        if(strcmp(name,names[i])==0)//name==names[i] ,when the length includes '\0' shit!
            return i;
    }
    return -1;
}
bool down(info a,info b)
{
    if(a.score!=b.score)//firstly compare the score
        return a.score>b.score;
    else if(a.net!=b.net)//secondly, if the scores are the same, compare the net goals
        return a.net>b.net;
    else if(a.win!=b.win)//If two teams have the same score 
    //and the same net goal, the one whose kicked in balls is bigger will be ahead.
        return a.win>b.win;
    else{//If two teams have the same score and the same net goal and the same kicked in balls, they will be outputed in alphabetic order.
        int t=strcmp(a.name,b.name);//a.name>b.name is wrong
        if(t==-1)
            return true;
        else
            return false;
    }
}
int main()
{
    int n;
    info ls[1000];
    char names[100][100]={"a","b","c"};
    char a[100],b[100],st[100];//char *a,*b,*st;
    int as,bs;
    while(~scanf("%d",&n)){//this is lost
        num=-1;
        memset(ls,0,sizeof(ls));
        for(int cnt=0;cnt<n*(n-1);cnt++){
            scanf("%s %s %s %d:%d",a,st,b,&as,&bs);//scanf("%s %s %s %d:%d",a,st,b,&as,&bs)
            int t1=in(names,a),t2=in(names,b);
            if(t1<0){
                num++;
                strcpy(ls[num].name,a);
                strcpy(names[num],a);
                ls[num].score=0,ls[num].win=0,ls[num].lose=0,ls[num].net=0;
            }
            if(t2<0){
                num++;
                strcpy(ls[num].name,b);
                strcpy(names[num],b);
                ls[num].score=0,ls[num].win=0,ls[num].lose=0,ls[num].net=0;
            }
            //add to ls
            t1=in(names,a),t2=in(names,b);
            if(as>bs)
                ls[t1].score+=3;
            else if(as<bs)
                ls[t2].score+=3;
            else if(as==bs){
                ls[t1].score+=1;
                ls[t2].score+=1;
            }
            ls[t1].win+=as;
            ls[t1].lose+=bs;
            ls[t2].win+=bs;
            ls[t2].lose+=as;
            ls[t1].net+=as-bs;
            ls[t2].net+=bs-as;
        }
    sort(ls,ls+n,down);
    for(int i=0;i<n;i++)
        printf("%s %d\n",ls[i].name,ls[i].score);
    printf("\n");
    }
    return 0;
}

H-请用sort

还是要用结构体排序,一个为满意值,一个为索引。最后把得出的索引放到新数组降序输出

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
int n,m,k;
int data[100],indice[100];
struct s
{
    double x;
    int indice;
}a[300];
bool down(int a,int b)
{
    return a>b;
}
bool cmp(struct s a,struct s b)
{
    if(a.x!=b.x)
        return a.x>b.x;
    else
        return a.indice<b.indice;
}
int main()
{
    while(cin>>n>>m>>k){
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++){
            for(int j=1;j<=m;j++){
                double t;
                cin >> t;
                a[j].indice=j;
                a[j].x+=t;
            }
        }
        sort(a+1,a+m+1,cmp);
        int ans[300];
        for(int i=1;i<=k;i++)
            ans[i]=a[i].indice;
        sort(ans+1,ans+k+1,down);
        for(int i=1;i<=k;i++){
            cout << ans[i];
            if(i!=k)
                cout<<" ";
        }
        cout<<endl;
    }
}

I - 辗转相除法求最小公倍数 (__int64)

https://blog.csdn.net/qq_26891045/article/details/51888524
The least common multiple (LCM) of a set of positive integers is the smallest positive integer which is divisible by all the numbers in the set. For example, the LCM of 5, 7 and 15 is 105.
Input
Input will consist of multiple problem instances. The first line of the input will contain a single integer indicating the number of problem instances. Each instance will consist of a single line of the form m n1 n2 n3 … nm where m is the number of integers in the set and n1 … nm are the integers. All integers will be positive and lie within the range of a 32-bit integer.
Output
For each problem instance, output a single line containing the corresponding LCM. All results will lie in the range of a 32-bit integer.
Sample Input
2
3 5 7 15
6 4 10296 936 1287 792 1
Sample Output
105
10296

//change int to __int64
#include<iostream>
#include<stdio.h>
using namespace std;
int gcd(int a,int b)
{
    int r;
    if(a<b){
        r=a;
        a=b;
        b=r;
    }
    r=a%b;
    while(r!=0){
        a=b;
        b=r;
        r=a%b;
    }
    return b;
}
int lcm(int a,int b)//lcm*gcd==a*b
{
    return a*b/gcd(a,b);
}
int main()
{
    int n,i,a,b,m;
    cin >>n;
    for(i=0;i<n;i++){
        cin >> m;
        cin >> a;
        m=m-1;
        while(m--){
            cin >> b;
            a=lcm(a,b);
        }
        cout << a << endl;
    }
    return 0;
}

J - Ignatius and the Princess IV (memset)

#include <iostream>
#include <string.h>
using namespace std;
int main()
{
    int map[100000];
    int n,i,t,flag;
    while(cin >> n){
        memset(map,0,sizeof(map));
        for(i=0;i<n;i++){
            cin >> t;
            map[t]=map[t]+1;
            if(map[t]>n/2)
                flag=t;
        }
        cout << flag << endl;
    }
    return 0;
}

K - next_permutation

要用康托展开?8会,直接用轮子Next_permutation

#include <iostream>
#include <algorithm>
#include <string.h>
using namespace std;
int main()
{
    int ls[1001];
    int n,m;
    while(cin>>n>>m)
    {
        int i;
        for(i=1;i<=n;i++)
            ls[i]=i;
        for(i=2;i<=m;i++)
            next_permutation(ls+1,ls+n+1);
        for(i=1;i<=n;i++){
            cout << ls[i];
            if(i!=n)
                cout<<" ";
        }
        cout << endl;
    }
    return 0;
}

L - Delta-wave(公式不会推)

https://blog.csdn.net/enjoying_science/article/details/38500755
公式不会推啊

X=(int)sqrt(n-1)+1;
Y= (n - (X-1)(X-1)+1)/2;
Z= (X
X - n)/2+1;

#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;\
void calculate(int ls[3],int n)
{
    ls[0]=(int)sqrt(n-1)+1;
    int x;
    x=ls[0];
    ls[1]=(n-(x-1)*(x-1)+1)/2;
    ls[2]=(x*x-n)/2+1;
}
int main()
{
    int p1,p2;
    while(cin>>p1>>p2){
        int l1[3],l2[3];
        calculate(l1,p1);
        calculate(l2,p2);
        cout<<abs(l1[0]-l2[0])+abs(l1[1]-l2[1])+abs(l1[2]-l2[2])<<endl;
    }
    return 0;
}

M - Digital Roots

防止给的数字太大,因此用getchar来进行第一次加和

#include <iostream>
#include <cstdio>
using namespace std;
int one(int x)
{
    int ans=0;
    while(1){
        if(x<10)
            return x;
        while(x>0){
            ans+=x%10;
            x/=10;
        }
        x=ans;
        ans=0;
    }
}
int main()
{
    while(1)
    {
        int sum=0;
        char c;
        //the number may be very big
        while((c=getchar())!='\n')
            sum+=(c-'0');
        if(sum==0)
            break;
        cout<<one(sum)<<endl;
    }
    return 0;
}

N - Bullseye

#include<iostream>
using namespace std;
int score(double x,double y)
{
    double rr=x*x+y*y;
    if(rr<=9) return 100;
    else if(rr<=6*6) return 80;
    else if(rr<=9*9) return 60;
    else if(rr<=12*12) return 40;
    else if(rr<=15*15) return 20;
    else return 0;
}
int main()
{
    double a,b;
    int suma=0,sumb=0;
    for(int i=0;i<6;i++){
        cin >> a >> b;
        if(a==-100)
            break;
        if(i<3)
            suma+=score(a,b);
        else if(i<6)
            sumb+=score(a,b);
        if(i==5){//i+++ -> i=i+1
            i=-1;
            if(suma>sumb)
                cout<<"SCORE: "<<suma<<" to "<<sumb<<", PLAYER 1 WINS."<<endl;
            else if(sumb>suma)
                cout<<"SCORE: "<<suma<<" to "<<sumb<<", PLAYER 2 WINS."<<endl;
            else
                cout<<"SCORE: "<<suma<<" to "<<sumb<<", TIE."<<endl;
            suma=0;
            sumb=0;
        }
    }
    return 0;
}

O - Dirichlet’s Theorem on Arithmetic Progressions

提交过程中sqrt参数为int不给过,记得强转为double或float

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
int prime(int x)
{
    int i;
    if(x==1)
        return 0;
    for(i=2;i<=sqrt(double(x));i++){
        if(x%i==0)
            return 0;
    }
    return 1;
}
int main()
{
    int a,d,n;
    while(cin>>a>>d>>n){
        if(n==0)
            break;
        if(prime(a))n--;
        while(n!=0){
            a+=d;
            if(prime(a))n--;
        }
        cout<<a<<endl;
    }
    return 0;
}

P - Reduced ID Numbers (不用memset就TE)

把mod函数里的check[300]={-1}改用memset就过了

#include <iostream>
#include <cmath>
#include <string.h>
using namespace std;
int find(int ls[],int e,int p)
{
    for(int i=0;i<=e;i++)
        if(p==ls[i])
            return 1;
    return 0;
}
int mod(int ls[],int k)
{
    int ans,i,check[300];//check[300]={-1};
    for(ans=1;;ans++){
        memset(check,-1,sizeof(check));//nothing here
        //int t=ls[0]%ans;   go wrong when the second and the third is the same
        for(i=0;i<k;i++){
            if(find(check,i,ls[i]%ans))
                break;
            check[i]=ls[i]%ans;
        }
        if(i==k)
            return ans;
    }
}
int main()
{
    int ls[300];
    int N;
    cin >> N;
    for(int qwe=0;qwe<N;qwe++)
    {
        int g;
        cin >> g;
        for(int i=0;i<g;i++){
            int t;
            cin >> t;
            ls[i]=t;
        }//stored in ls
        cout << mod(ls,g) << endl;
    }
    return 0;
}

Q-大数幂

下面是自己写的,怎么都过不去,难受的一p
java直接调包过就完事了
https://blog.csdn.net/u012340794/article/details/50544377

import java.util.*;
import java.math.*;
public class Main {
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n;
		String r;
		while(sc.hasNext()){
			r = sc.next();
			n = sc.nextInt();
			BigDecimal bd = new BigDecimal(r);
			BigDecimal result = bd.pow(n);
			//BigDecimal:不可变的、任意精度的有符号十进制数
			
			r = result.stripTrailingZeros().toPlainString();
			//stripTrailingZeros():返回数值上等于此小数,但从该表示形式移除所有尾部零的 BigDecimal
			//toPlainString():将BigDecimal转换为字符串
			
			if(r.startsWith("0")){
				//去掉开头的0
				r=r.substring(1);
			}
			System.out.println(r);
		}
	}
}
#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int zero(string s)
{
    if(s[0]=='0'&&s[2]=='0'&&s[3]=='0'&&s[4]=='0'&&s[5]=='0')
        return 1;
    else
        return 0;
}
int tn(char c)
{
    return c-'0';
}
char ttf(int x)
{
    return x+'0';
}
string change(string s)
{
    int pflag=1,bflag=-1,i;//123
    for(int i=0;i<int(s.length());i++)
        if(s[i]=='.'){
            pflag=0;
            break;
        }
    if(pflag)
        s=s+".0";
    //0002.0
    char str[100];
    int len=s.length();
    for(i=0;i<int(s.length());i++){
        str[i]=s[i];
        if(str[i]!='0'&&bflag==-1)
            bflag=i;
    }
    s="";
    if(str[bflag]=='.')
        s="0";
    for(i=bflag;i<len;i++)
        s+=str[i];
    for(i=int(s.length())-1;i<5;i++)
        s+='0';
    return s;
}
string BNMultipy(string num1,string num2)
{
    int len1=num1.length(),len2=num2.length();
    int fans[1000],ans[1000],i,j,flag;
    string anss="";
    memset(ans,0,sizeof(ans));
    memset(fans,0,sizeof(fans));
    for(i=len2-1,flag=999;i>-1;i--,flag--){
        int tem,f=flag;
        for(j=len1-1;j>-1;j--){
            tem=tn(num2[i])*tn(num1[j]);
            fans[f--]+=tem;
        }
    }
    int carry=0;
    for(i=999;i>-1;i--){
        ans[i]+=(carry+fans[i])%10;
        carry=(carry+fans[i])/10;//carry=fans[i]/10;
    }
    int yes=0;
    for(i=0;i<1000;i++){
        if(yes)
            anss+=ttf(ans[i]);
        else{
            if(ans[i]!=0)
                anss+=ttf(ans[i]),yes=1;
        }
    }
    return anss;
}
int main()
{
    string s;
    int n;
    while(cin>>s>>n){
        s=change(s);
        int i,point;//有可能没有小数点
        if(zero(s)){
            cout<<0<<endl;
            continue;
        }
        else if(n==0){
            cout<<1<<endl;
            continue;
        }
        if(n==1&&s[0]=='0'){
            int oo;
            for(i=5;i>-1;i--)
                if(s[i]!='0'){
                    oo=i;
                    break;
                }
            for(i=1;i<=oo;i++)
                cout<<s[i];
            cout<<endl;
            continue;
        }
        string a="",ans="";
        for(i=0;i<6;i++){//i<int(s.length()) is wrong
            if(s[i]=='.')
                point=5-i;
            else
                a+=s[i];
        }
        string b=a;
        point=point*n;
        for(i=0;i<n-1;i++)
            a=BNMultipy(a,b);
        int len=a.length();
        for(i=0;i<len;i++){
            if(i==len-point&&len>=point)
                ans+=".",ans+=a[i];
            else
                ans+=a[i];
        }
        len=ans.length();
        for(i=len-1;;i--){
            if(ans[i]=='0'){
                ans[i]='\0';
                if(ans[i-1]=='.')
                    ans[i-1]='\0';
            }
            else
                break;
        }
        if(s[0]=='0'){
            cout<<".";
            for(i=0;i<4*n-int(ans.length());i++)
                cout<<"0";
        }
        cout<<ans<<endl;
    }
    return 0;
}

R - Joseph

此题打表过,否则TE
好人的范围不会变,去除的坏人为5678中的7,则坏人又变为567

#include<iostream>
#include<string.h>
using namespace std;
/*----------------------打表代码---------------------------*/
int da_biao(void)
{
    int k,cnt,i;
    while(cin>>k)
    {
        if(!k)break;
        int ans=1,flag;
        while(ans++){
            int p=1;
            flag=0;
            cnt=2*k;
            for(i=1;i<=k;i++){
                int t=(p+ans-1)%cnt;
                if(t>0&&t<=k){
                    flag=1;
                    break;
                }
                cnt--;
                if(t==0)//如果去掉了最后一个人,那么下次从第一个人开始
                    p=1;
                else//否则上个被杀的人的编号就是下次开始的编号
                    p=t;
            }
            if(flag)continue;
            else break;
        }
        cout<<ans<<endl;
    }
    return 0;
}
/*----------------------打表代码---------------------------*/
int main()
{
    int k;
    int ls[14]={2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881};
    while(cin >> k){
        if(k==0)
            break;
        cout<<ls[k-1]<<endl;
    }
    return 0;
}

S - Counterfeit Dollar

https://blog.csdn.net/qq_41045071/article/details/81740569
用枚举解决,每个假设都过一遍

#include <iostream>
#include <string.h>
#include <string>
using namespace std;
string s1[3],s2[3],s3[3];
int statu[12],k;
int judge()
{
    int i;
    for(i=0;i<3;i++){//3遍必须判断完才能确认
        int j;
        int left=0,right=0;
        for(j=0;j<s1[i].length();j++)
            left+=statu[s1[i][j]-'A'];
        for(j=0;j<s2[i].length();j++)
            right+=statu[s2[i][j]-'A'];
        if(left>right&&s3[i]!="up")
            return 0;
        if(left<right&&s3[i]!="down")
            return 0;
        if(left==right&&s3[i]!="even")
            return 0;
    }
    return 1;//3次判断没有问题时才返回true
}
int main()
{
    int n;
    cin>>n;
    while(n--){
        for(int i=0;i<3;i++)
            cin>>s1[i]>>s2[i]>>s3[i];
        memset(statu,0,sizeof(statu));
        for(k=0;k<12;k++){
            statu[k]=1;
            if(judge())break;
            statu[k]=-1;
            if(judge())break;
            statu[k]=0;
        }
        string a;
        if(statu[k]>0)a="heavy";
        else if(statu[k]<0)a="light";
        char ans=k+'A';
        cout<<ans<<" is the counterfeit coin and it is "<<a<<"."<<endl;
    }
    return 0;
}

U - Long Distance Racing

#include<iostream>
using namespace std;
char road[100001];
int N,T,U,F,D;
int main()
{
    int cnt=0;
    cin>>N>>T>>U>>F>>D;
    for(int i=0;i<T;i++)
        cin>>road[i];
    int sum=0;
    for(;;cnt++){
        char d=road[cnt];
        if(d=='f')
            sum+=2*F;
        else
            sum+=D+U;
        if(sum>N)
            break;
    }
    cout<<cnt<<endl;
    return 0;
}

W - The Circumference of the Circle

R=abc/4/S a,b,c为边长,S为三角形面积

#include<iostream>
#include<cmath>
#include<cstdio>
#define PI 3.141592653589793
using namespace std;
int main()
{
    double x1,x2,x3,y1,y2,y3;
    while(cin>>x1>>y1>>x2>>y2>>x3>>y3){
        double S=((x2-x1)*(y3-y1)-(x3-x1)*(y2-y1))/2;
        double abc=sqrt((x2-x1)*(x2-x1)+(y2-y1)*(y2-y1))\
                   *sqrt((x3-x1)*(x3-x1)+(y3-y1)*(y3-y1))\
                   *sqrt((x2-x3)*(x2-x3)+(y2-y3)*(y2-y3));
        double R=abc/4/S;
        printf("%.2lf\n",fabs(2*PI*R));
    }
    return 0;
}

X - Specialized Four-Digit Numbers

掌握进制转化的基本原理,设计digits函数转任何进制数字digits之和

#include <iostream>
using namespace std;
int digits(int n,int x)
{
    int rest,sum=0;
    while(n>0){
        rest=n%x;
        sum+=rest;
        n/=x;
    }
    return sum;
}
int main()
{
    for(int i=2992;i<=9999;i++)
        if(digits(i,10)==digits(i,12)&&digits(i,10)==digits(i,16))
            cout<<i<<endl;
    return 0;
}

Y - A == B ? (数组开小导致超时)

//TE
#include<stdio.h>
#include<string.h>
void f(char *a)
{
    int i,p=0;
    for(i=0;i<strlen(a);i++){
        if(a[i]=='.'){
            p=i;
            break;
        }
    }
    if(p){
        for(i=strlen(a)-1;i>p;i--){
            if(a[i]=='0')
                a[i]='\0';
            else
                break;
        }
        if(p==i){//the situation is "4."
            a[p]='\0';//change "4." to "4"
        }
    }
}
int main()
{
    char a[100000]={0},b[100000]={0}; //char a[200],b[200];导致TE
    while(scanf("%s%s",a,b)!=EOF){
        f(a),f(b);
        if(strcmp(a,b)==0)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}
//TE

Z - A + B Problem II(大数加法)

注意输出是相邻之间有空行

sample out
Case 1:
1 + 2 = 3

Case 2:
112233445566778899 + 998877665544332211 = 1111111111111111110

#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int max(int a,int b)
{
    return a>b?a:b;
}
int change(char c)
{
    int i;
    char s[]={'0','1','2','3','4','5','6','7','8','9'};
    for(i=0;i<10;i++){
        if(c==s[i])
            break;
    }
    return i;
}
int main()
{
    int na[1001]={0},nb[1001]={0},ns[1001]={0};
    int n,nn,len,salen,sblen;
    string sa,sb;
    cin >> n;
    for(nn=0;nn<n;nn++)
    {
        memset(na,0,sizeof(na));
        memset(nb,0,sizeof(nb));
        memset(nb,0,sizeof(nb));
        cin >> sa >> sb;
        salen=sa.length();
        sblen=sb.length();
        len=max(salen,sblen);
        int j=1000,i;
        for(i=salen-1;i>=0;i--)
            na[j--]=change(sa[i]);//na[j--]=sa[i]-'0'
        j=1000;
        for(i=sblen-1;i>=0;i--)
            nb[j--]=change(sb[i]);//nb[j--]=sb[i]-'0'
        int unit,buf=0,sum;
        for(i=1000;i>=1001-len-1;i--){//i=len-1 is wrong
            sum=na[i]+nb[i]+buf;
            unit=sum%10;
            ns[i]=unit;
            buf=sum/10;
        }
        cout << "Case " << nn+1 << ":" << endl;
        cout << sa << " + " << sb << " = ";
        if(buf)cout<<buf;
        for(i=1001-len;i<1001;i++)
            cout<<ns[i];
        cout<<endl;
        if(nn!=n-1)
            cout<<endl;
    }
    return 0;
}

A - stl 的 map

map的基本操作,如果要对value排序则把每个pair存入vector,再用sort即可

#include <iostream>
#include <string>
#include <map>
using namespace std;
map<string,int>ma;
map<string,int>::iterator it;
int main()
{
    int n;
    while(cin>>n){
        if(n==0)
            break;
        while(n--){
            string s;
            cin>>s;
            if(ma.find(s)==ma.end())
                ma.insert(pair<string,int>(s,1));
            else
                ma[s]++;
        }
        int maxInt=0;
        string ans;
        for(it=ma.begin();it!=ma.end();it++)
            if((*it).second>maxInt)
                maxInt=(*it).second,ans=(*it).first;
        cout<<ans<<endl;
        ma.clear();
    }
    return 0;
}

B - stl 的 map

#include <iostream>
#include <cstring>
#include <string>
#include <map>
#include <cstdio>
using namespace std;
int main()
{
    map<string,string> ma;
    string s1,s2;
    cin>>s1;//START
    while(cin>>s1&&s1!="END"){
        cin>>s2;
        ma[s2]=s1;
    }
    cin>>s1;//START
    getchar();//cin没有读取\n,不getchar掉gets会得到回车并把他变为\0
    while(1){
        char s[3001];
        gets(s);
        if(strcmp(s,"END")==0)
            break;
        int len=strlen(s),i;
        string t="";
        for(i=0;i<len;i++){
            if(s[i]<'a'||s[i]>'z'){
                if(ma[t]!="")//if(t!="") 例如im单词不做改变,用else输出原单词
                    cout<<ma[t];
                else
                    cout<<t;
                cout<<s[i];
                t="";
            }
            else
                t+=s[i];
        }
        cout<<endl;
    }
/*    map<string,string>::iterator it;
    for(it=ma.begin();it!=ma.end();it++)
        cout<<it->first<<" "<<it->second<<endl;
*/
    return 0;
}

C - stl 的 map

map的双层嵌套
vector和map内置数据默认都是0

#include <iostream>
#include <string>
#include <map>
#include <cstdio>
using namespace std;
int main()
{
    map<string,map<string,int> > mp;
    string fruit,province;
    int amount;
    int n,m;
    cin>>n;
    while(n--){//下一行n就减去1
        mp.clear();
        cin>>m;
        while(m--){
            cin>>fruit>>province>>amount;
            mp[province][fruit]+=amount;//mp内置数据类型默认为0;
        }
        map<string,map<string,int> >::iterator p1;
        map<string,int>::iterator p2;
        for(p1=mp.begin();p1!=mp.end();p1++){
            cout<<p1->first<<endl;
            for(p2=(p1->second).begin();p2!=(p1->second).end();p2++){
                cout<<"   |----"<<p2->first<<"("<<p2->second<<")"<<endl;
            }
        }
        if(n!=0)
            cout<<endl;
    }
}

D - stl 的 map

根本不用map啊,就是个物理题,代码直接抄了
https://blog.csdn.net/zugofn/article/details/52207470

E - 贪心

oj上语言选c++时,用map的greater要加头文件functional
贪心,先加比值最大的就可以了
注意坑点:JavaBean有可能是免费的

此题除了要满足例子以外,还要满足一些条件才能真正算ac:
0 1
1 0
1.000

1 0
0.000

5 4
10000 5
2000 2
100 0
300 0
10400.000

#include <iostream>
#include <map>
#include <cstdio>
#include <functional>
using namespace std;
int main()
{
    double J[1001],F[1001];
    int m,n;
    while(cin>>m>>n){
        double ans=0;
        if(m==-1)
            break;
        int i=-1;
        map<double,int,greater<double> > mp;//第一个是比值,第二个是索引,第三个是降序
        map<double,int>::iterator it;
        while(n--){
            ++i;
            cin>>J[i]>>F[i];
            if(F[i]==0){
                ans+=J[i];
                continue;
            }
            mp[J[i]/F[i]]=i;
        }
        for(it=mp.begin();it!=mp.end();it++){
            int j=it->second;
            if(m>F[j]){
                ans+=J[j];
                m-=F[j];
            }
            else{
                ans+=m/F[j]*J[j];
                break;
            }
        }
        printf("%.3lf\n",ans);
    }
    return 0;
}

F - 递推

https://blog.csdn.net/destiny1507/article/details/81228610
主要通过交点数来判断多少面
对于直线分割平面:原本有n直线,每多一条直线,就多了n个交点,就多了n+1个区域
因此对于直线分割 f(n)=f(n-1)+n+1
对于折线分割平面:原本有n折线,每多一个折线,就多了4*(n-1)个交点,就多了4n-3个区域
因此对于折线分割 f(n)=f(n-1)+4*n-3

#include <iostream>
using namespace std;
int f(int n)
{
    if(n==1)
        return 2;
    else
        return f(n-1)+4*n-3;
}
int main()
{
    int n,m;
    cin>>n;
    while(n--){
        cin>>m;
        cout<<f(m)<<endl;
    }
    return 0;
}

G - stl 的 优先队列

直接硬刚内存不够,先把所有的都加一遍,再减去最大的和最小的,优先队列自动保存最大最小。
https://blog.csdn.net/zyd8888102/article/details/50760155

#include <iostream>
#include <cstdio>
#include <queue>
#include <vector>
#define ll long long
using namespace std;
int main()
{
    int n,n1,n2;
    while(scanf("%d%d%d",&n1,&n2,&n)&&n1!=0&&n2!=0&&n!=0){
        priority_queue<ll,vector<ll>,greater<ll> > big;//先出大的,第二个参数vector不能少
        priority_queue<ll,vector<ll>,less<ll> > small;
        ll sum=0,a;
        int i;
        for(i=0;i<n;i++){
            scanf("%lld",&a);
            sum+=a;
            big.push(a);
            if(big.size()>n1)
                big.pop();
            small.push(a);
            if(small.size()>n2)
                small.pop();
        }
        for(i=0;i<n1;i++){
            sum-=big.top();
            big.pop();
        }
        for(i=0;i<n2;i++){
            sum-=small.top();
            small.pop();
        }
        int t=n-n1-n2;
        printf("%.6f\n",1.0*sum/t);
    }
}

I - Safecracker

模拟就行,至于字符串和char连接时,至少是ans+=char||ans=ans+char

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#include <string>
using namespace std;
int cmp(int a, int b)
{
    return a>b;
}
int pow(int m, int n)
{
    int ans=m;
    while(--n)
        ans*=m;
    return ans;
}
char ch(int x)
{
    string ans="ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    return ans[x-1];
}
string judge(int tar, int ls[], int len)
{
    int v,w,x,y,z;
    string ans="";
    //v - w^2 + x^3 - y^4 + z^5 = target
    for(v=0;v<len;v++)
        for(w=0;w<len;w++)
            for(x=0;x<len;x++)
                for(y=0;y<len;y++)
                    for(z=0;z<len;z++)
                    {
                        if(v==w||w==x||x==y||y==z)continue;
                        if(pow(ls[v],1)-pow(ls[w],2)+pow(ls[x],3)-pow(ls[y],4)+pow(ls[z],5)==tar){
                            ans+=ch(ls[v]),ans+=ch(ls[w]),ans+=ch(ls[x]),ans+=ch(ls[y]),ans+=ch(ls[z]);
                            return ans;
                        }
                    }
    return "no solution";;
}
int main()
{
    int tar;
    string str;
    while(cin>>tar>>str&&tar){
        int ls[20];
        int len=str.length();
        for(int i=0;i<len;i++){
            ls[i]=str[i]-'A'+1;
        }
        sort(ls,ls+len,cmp);
        string ans=judge(tar,ls,len);
        cout<<ans<<endl;
    }
    return 0;
}

J - 归并排序求逆序对

对于[7,8],[5,6]因为逆序对是左大于右,对于两边已经排好序的数组,如果7大于5,说明7的右边所有数(8)也大于5,因此逆序数加的是mid-i+1
注意归并的l,r,k等数据的初始化

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
char b[51];
int cnt;
struct node{
    char str[51];
    int inversions,origin;
};
bool cmp(node a, node b){
    if(a.inversions==b.inversions)return a.origin<b.origin;
    else return a.inversions<b.inversions;
}
void combine(char t[51], int l, int r)
{
    int m=(l+r)/2,i=l;
    int j=m+1,k=l;
    //stored in b
    while(i<=m&&j<=r){
        if(t[i]>t[j]){
            b[k++]=t[j++];
            cnt+=m-i+1;
        }
        else
            b[k++]=t[i++];
    }
    while(i<=m&&j>r){
        b[k++]=t[i++];
    }
    while(i>m&&j<=r){
        b[k++]=t[j++];
    }
    for(i=l;i<=r;i++)
        t[i]=b[i];
}
void merge_sort(char t[51], int l, int r)
{
    if(l<r){
        int m=(l+r)/2;
        merge_sort(t,l,m);
        merge_sort(t,m+1,r);
        combine(t,l,r);
    }
}
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    node ls[101];
    int n,m,i;
    while(cin>>n>>m){
        for(i=0;i<m;i++){
            cin>>ls[i].str;
            ls[i].origin=i;
        }
        for(i=0;i<m;i++){
            cnt=0;
            char p[51];
            memcpy(p,ls[i].str,sizeof(ls[i].str));
            merge_sort(p,0,n-1);
            ls[i].inversions=cnt;
        }
        sort(ls,ls+m,cmp);
        for(i=0;i<m;i++)
            cout<<ls[i].str<<endl;
    }
    return 0;
}

K - Big Number

求阶乘的位数
lg10=1 lg100=2 lg20>1 位数等于((int)lgN!)+1–>lg1+lg2+…+lgN+1

#include <iostream>
#include <cmath>
#define Int __int64
using namespace std;
int main()
{
    int n;
    Int a;
    cin>>n;
    while(n--){
        double sum=0;
        cin>>a;
        for(int i=2;i<=a;i++)
            sum+=log10(i);
        cout<<int(sum)+1<<endl;
    }
    return 0;
}

L - 卡特兰数

基于X题

N - A + B Again(补码什么的搞不懂)

十六进制负数时时补码形式,需要转正数

#include <iostream>
#include <cstdio>
#define Int __int64
using namespace std;
int main()
{
    Int a,b;
    while(~scanf("%I64X%I64X",&a,&b)){
        if(a+b>=0)
            printf("%I64X\n",a+b);
        else
            printf("-%I64X\n",-(a+b));
    }
    return 0;
}

P - 贪心

不能以deadline为第一升序,以score为第二降序,不是最优解
应该先看score而不是ddl
https://blog.csdn.net/mengxiang000000/article/details/50372828
47 26 45 34 13 42 61
47 我们用一个数组来保存预定的天数,从大开始看,先是47,预定到第4天
26 第四天我们预订好了,再看下一个26,第二天没有定,第二天预定了
45 第4天预定过了啊,分也挺大,就放到第三天
34 第三天也预订了,第二天也定了,就第一天吧
13 第1天预定过了,没办法,扣分吧
42 第4天因为排序了所以分更低,前几天也预订了,扣分吧
61 第6天没人嗷,预订了
所以是3+2为5分
注意:数组写大小的时候必须是常量表达式,不能是N这些的,否则C++,G++都CE

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
struct sub
{
    int day,score;
};
bool cmp(sub a,sub b)
{
    if(a.score!=b.score)return a.score>b.score;
    else return a.day<b.day;
}
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    int T,N;
    cin>>T;
    while(T--){
        int i;
        cin>>N;
        int data[1001]={0};//data[j]指向的是第j天,除去了0,所以是n+1
        sub ls[1000];
        for(i=0;i<N;i++)
            cin>>ls[i].day;
        for(i=0;i<N;i++)
            cin>>ls[i].score;
        sort(ls,ls+N,cmp);
//      for(i=0;i<N;i++)printf("(ddl: %d,score: %d)\n",ls[i].day,ls[i].score);
        int ans=0;
        for(i=0;i<N;i++){
            int j=ls[i].day;
            while(j){//预定天数
                if(data[j]==0){
                    data[j]=1;
                    break;
                }
                j--;
            }
            if(j==0)//没有预定到天数,只能扣分了
                ans+=ls[i].score;
        }
        cout<<ans<<endl;
    }
    return 0;
}

Q - 贪心

dps/hp作为排序依据就好了

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
struct enemy
{
    int hp,dps;
};
bool cmp(enemy a, enemy b)
{
    if(1.0*a.dps/a.hp>1.0*b.dps/b.hp)
        return true;
    else
        return false;
}
int main()
{
    enemy ls[21];
    int n,i;
    while(cin>>n){
        int otd=0,ans=0;//OneTurnDamage
        for(i=0;i<n;i++)
            cin>>ls[i].dps>>ls[i].hp,otd+=ls[i].dps;
        sort(ls,ls+n,cmp);
//      for(i=0;i<n;i++)printf("(dps: %d,hp: %d)\n",ls[i].dps,ls[i].hp);
        for(i=0;i<n;i++){
            ans+=ls[i].hp*otd;
            otd-=ls[i].dps;
        }
        cout<<ans<<endl;
    }
    return 0;
}

W - stl 的 优先队列 (hdu1896)

https://www.cnblogs.com/stepping/p/5669066.html
https://blog.csdn.net/liujian20150808/article/details/51038520
题目说的是遇到的第k个石头,k是不是奇数,而不是位置是奇数
再有,如果一个地方有3个石头,是3个石头都处理,而不是只看Di最大的石头
优先队列无法嵌套到数组内,所以选择嵌套到map中
但其实一个pq就够了,要重写仿函数,不懂啥意思,用就完事了
我们不用关心pq里还有没有数据,只关心最远到哪里,所以可以一直pop到空

#include <iostream>
#include <queue>
#include <map>
#include <vector>
using namespace std;
struct stone
{
    int Pi,Di;
};
struct cmp//重写仿函数
{
    bool operator()(stone a, stone b){
        if(a.Pi!=b.Pi)return a.Pi>b.Pi;//末端出
        else return a.Di>b.Di;
    }
};
int main()
{
    priority_queue<stone,vector<stone>,cmp> road;
    int T,N;
    cin>>T;
    while(T--){
        cin>>N;
        stone x;
        while(N--){
            cin>>x.Pi>>x.Di;
            road.push(x);
        }
        int cnt=0;
        while(!road.empty()){
            x=road.top();
            road.pop();
            cnt++;
            if(cnt%2==1){//odd stone throw it and if it is even, the stone has been popped
                x.Pi+=x.Di;
                road.push(x);
            }
        }
        cout<<x.Pi<<endl;
    }
    return 0;
}

X - 栈

https://blog.csdn.net/Dextrad_ihacker/article/details/50083845
只有A->C,C->B两种方案,因此有以下3种可能,主要看能不能满足出站顺序:
1.A->C->B:进站的直接出站A[I]==B[I]
2.C->B:站里停车,出站的对应站中的top,top出站(先看出站)
3.A->C:进站的无法对应出站的 且 站里没车,先进站等着,s.push(A[i])
以上三条是规则,要是都不满足则输出NO

但是不知道为什么用转Int数组存就WA,直接字符数组存AC

#include <iostream>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
using namespace std;
int main()
{
    vector<string> ans;
    //int origin[2000],target[2000];//A是进站车号,B是出站车号
    char origin[2000],target[2000];
    int N,A,B;
    string in,to;
    while(cin>>N){
  /*    cin>>in>>to;
        for(int i=1;i<=N;i++)
            origin[i]=in[i-1]-'0',target[i]=to[i-1]-'0';
            */
        stack<int> s;
        //列举三种可能
        scanf("%s %s",origin+1,target+1);
        A=1,B=1;
        int ok=1;

        while(B<=N){//出站全部符合
            if(origin[A]==target[B]){
                A++;
                B++;
                ans.push_back("in");
                ans.push_back("out");
            }
            else if(!s.empty()&&s.top()==target[B]){//优先考虑出站,假设都进到站了
                s.pop();
                B++;
                ans.push_back("out");
            }
            else if(A<=N){
                s.push(origin[A]);
                A++;
                ans.push_back("in");
            }
            else{
                ok=0;
                break;
            }
        }
        if(!ok)cout<<"No."<<endl;
        else{
            cout<<"Yes."<<endl;
            for(int i=0;i<ans.size();i++)
                cout<<ans[i]<<endl;
        }
        cout<<"FINISH"<<endl;
        ans.clear();
    }
    return 0;
}

Z - Bitset

十进制转二进制,用位运算
对于4(0b100)先和1进行与运算,因为1(0b1)所以只看最后一位,如果是1结果为1,否则结果为0,运算完后再右移一位。

#include <iostream>
#include <string>
using namespace std;
int main()
{
    int n;
    while(cin>>n){
        string s="";
        while(n){
            if(n&1)
                s='1'+s;
            else
                s='0'+s;
            n>>=1;
        }
        cout<<s<<endl;
    }
}

A - 贪心

首先对结构体按照开始时间升序排列。这样从0~N找时只要判断该奶牛的起始时间是否比前面每个畜栏的最小的终止时间大,如果大的话,就可以安排在其后面产奶,并且更新该畜栏的终止时间。

struct node {
    int x,y;
    friend bool operator <(node p,node q) {
        return p.x>q.x; // >号代表从小到大排序 (按照x排序)
    }
}nod;

这样子写是从小到大,很迷。
优先队列维护的是每个房间最后一个cow。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <queue>
using namespace std;
struct cow
{
    int begin,end,pos;
    friend bool operator <(cow a, cow b){
        if(a.end==b.end)
            return a.begin>b.begin;//从小到大?
        return a.end>b.end;//从小到大
    }
};
bool cmp(cow a, cow b)
{
    return a.begin<b.begin;
}
int main()
{
    cow cows[50005];
    int index[50005];
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cows[i].pos=i;
        cin>>cows[i].begin>>cows[i].end;
    }
    sort(cows+1,cows+1+n,cmp);
    priority_queue<cow> pq;
    int cnt=1;
    pq.push(cows[1]);
    index[cows[1].pos]=1;
    for(int j=2;j<=n;j++){
        cow temp=pq.top();
        if(cows[j].begin<=temp.end){//如果所有最早结束房子都不能用的话,重开房子
            cnt++;
            index[cows[j].pos]=cnt;
        }
        else{
            index[cows[j].pos]=index[temp.pos];
            pq.pop();//这个房子结束时间不是temp牛的了,而是新牛的
        }
        pq.push(cows[j]);//选牛,新牛添加到原来末,不选牛还是要新开
    }
    cout<<cnt<<endl;
    for(int i=1;i<=n;i++){
        cout<<index[i]<<endl;
    }
    return 0;
}

B - 优先队列

题意:有若干任务,给出任务的id(各个任务唯一)和执行间隔时间(每个任务不唯一);要求按照执行时间来输出前k个任务的id号; 当两个任务在同一个时间执行时,先输出id小的。
每次执行完后给原时间上加上。

#include <iostream>
#include <string>
#include <queue>
using namespace std;
struct node
{
    int id,time,total;
    friend bool operator<(node a, node b){
        if(a.total==b.total)
            return a.id>b.id;//实际是a的id更小
        return a.total>b.total;
    }
};
int main()
{
    string ins;
    int cnt;
    priority_queue<node> pq;
    while(cin>>ins&&ins!="#"){
        node temp;
        cin>>temp.id>>temp.time;
        temp.total=temp.time;
        pq.push(temp);
    }
    cin>>cnt;
    for(int i=1;i<=cnt;i++){
        cout<<pq.top().id<<endl;
        node temp=pq.top();
        pq.pop();
        temp.total+=temp.time;
        pq.push(temp);
    }
    return 0;
}

C - map

简单map

#include <iostream>
#include <map>
#include <cstdio>
#include <string>
using namespace std;
map<string,string> mp;
int main()
{
    string ori,loc;
    while(cin>>ori){
        if(getchar()=='\n')break;
        /*对于cat asd,ori为cat,a为空格,loc为asd
        * 这段仍然无法判断空行
        * 对于asd,ori为asd,a为\n,识别出已经过空行,ori保存的是第一个单词
        */
        cin>>loc;
        mp[loc]=ori;
    }
    string a;
    if(mp[ori]=="")cout<<"eh"<<endl;
    else cout<<mp[ori]<<endl;
    while(cin>>a){
        if(mp[a]=="")cout<<"eh"<<endl;
        else cout<<mp[a]<<endl;
    }
    return 0;
}

E - 二分

设用风干的时间是x,吹风机是y,总时间为t,吹风机每分钟吹k,则有公式:
(1)x+y=t…(2)ai<=x+y*k--------->>>>(y>=(ai-t)/(k-1)
自己适合模拟一下就知道判断条件是r-l>1和r为最终结果
cin,cout会TE

#include <cmath>
#include <algorithm>
#include <iostream>
#include <cstdio>
#define ll long long
using namespace std;
int judge(ll ls[], ll len, ll time, ll k)
{
    //y>=(ai-t)/(k-1)
    ll temp=0;
    for(int i=1;i<=len;i++)
        if(ls[i]>time)//否则直接风干了
            temp+=ceil((ls[i]-time)*1.0/(k-1));
    if(temp>time)
        return 0;
    return 1;
}
int main()
{
    ll n,k,ls[100009];
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
        scanf("%lld",&ls[i]);
    ll *p=max_element(ls+1,ls+n+1);
    ll Max=*p;
    scanf("%d",&k);
    if(k==1){
        printf("%d\n",Max);
        return 0;
    }
    ll l=1,r=Max,mid;
    while(r-l>1){
        mid=(r+l)/2;
        if(judge(ls,n,mid,k))
            r=mid;
        else
            l=mid;
    }
    printf("%lld",r);
    return 0;
}

F - 二分

为了提高精度一开始不要乘pi

#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const double pi=3.14159265359;
const double limit=1e-7;
int main()
{
    int T;
    cin>>T;
    while(T--){
        double N,F;
        double s[10009];
        double Max=0;
        cin>>N>>F;
        for(int i=1;i<=N;i++){
            double r;
            cin>>r;
            s[i]=r*r;
            Max=max(s[i],Max);
        }
        double l=0,r=Max,mid;
        while(r-l>limit){
            mid=(l+r)/2;
            int cnt=0;
            for(int i=1;i<=N;i++)
                cnt+=(int)s[i]/mid;
            if(cnt>F)
                l=mid;
            else
                r=mid;
        }
        printf("%.4lf\n",r*pi);
    }
    return 0;
}

G - 二分

分段最大化最小化用二分
小疑问:最后一个数字会不会比mid大?

题意为给定一个n个数组成的序列,划分为m个连续的区间,每个区间所有元素相加,得到m个和,m个和里面肯定有一个最大值,我们要求这个最大值尽可能的小。
用二分查找可以很好的解决这个问题。这类问题的框架为,找出下界left和上界right, while(left< right), 求出mid,看这个mid值是符合题意,继续二分。最后right即为答案。
本题中的下界为n个数中的最大值,因为这时候,是要划分为n个区间(即一个数一个区间),left是满足题意的n个区间和的最大值,上届为所有区间的和,因为这时候,是要划分为1个区间(所有的数都在一个区间里面), 1<=m<=n, 所以我们所要求的值肯定在 [left, right] 之间。对于每一个mid,遍历一遍n个数,看能划分为几个区间,如果划分的区间小于(或等于)给定的m,说明上界取大了, 那么 另 right=mid,否则另 left=mid+1.
原文:https://blog.csdn.net/sr_19930829/article/details/38065689

一开始left=mid卡TE,因为1,3mid一直是2,判断条件是right>left,left=mid+1

#include <iostream>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
int main()
{
    int N,M;
    int cost[100010];
    scanf("%d%d",&N,&M);
    int left=-1,right=0;
    for(int i=1;i<=N;i++){
        scanf("%d",&cost[i]);
        left=max(cost[i],left);//保证left大于最大的
        right+=cost[i];
    }
    while(right>left){//right-left>1
        int mid=(left+right)/2;
        int cnt=0,sum=0;
        for(int j=1;j<=N;j++){
            if(sum+cost[j]>mid){//>= is wrong 如果前面和这个大于了,前面的组一队,这个单创建一队
                sum=cost[j];
                cnt++;
            }
            else
                sum+=cost[j];
        }
        cnt++;//最后一部分没有计算 例如1 1 1 1最大容量也为1
        if(cnt>M)
            left=mid+1;
        else
            right=mid;
    }
    printf("%d\n",right);
    return 0;
}

I - 素数环 搜索

http://acm.hdu.edu.cn/showproblem.php?pid=1016
对第一个数后面的dfs

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int book[1000],ans[1000],n;
const int prime[51]={0,0,1,1,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0,1,0,0,0,1,0,1,0,0,0,1,0,0,0};
int is(void);
void dfs(int step)
{
    if(prime[ans[step-1]+ans[step-2]]==0&&step>2)return;
    if(step>n){
        if(prime[ans[1]+ans[n]]==0)return;
        for(int i=1;i<=n;i++){
            cout<<ans[i];
            if(i<n)cout<<" ";
        }
        cout<<endl;
        return;
    }
    for(int i=2;i<=n;i++){
        if(book[i]==0){
            ans[step]=i;
            book[i]=1;
            dfs(step+1);
            book[i]=0;
        }
    }
}
int is(void)
{
    if(prime[ans[1]+ans[n]]==0)return 0;
    for(int i=1;i<n;i++){
        if(prime[ans[i]+ans[i+1]]==0)return 0;
    }
    return 1;
}
int main()
{
    ans[1]=1;
    int cnt=1;
    while(cin>>n){
        cout<<"Case "<<cnt++<<":"<<endl;
        dfs(2);
        cout<<endl;
    }
    return 0;
}

K - 搜索

dfs只求一次联通块,然后算周长就ok

#include <cstdio>
#include <string.h>
using namespace std;
char pic[25][25];
int idx[25][25];
int m,n,a,b;
void dfs(int x, int y)
{
    if(x<1||x>m||y<1||y>n)return ;
    if(idx[x][y]==1||pic[x][y]=='.')return ;
    idx[x][y]=1;
    for(int dx=-1;dx<=1;dx++)
        for(int dy=-1;dy<=1;dy++)
            if(dy!=0||dx!=0)dfs(x+dx,y+dy);
}
int len(void)
{
    int ans=0;
    for(int x=1;x<=m;x++)
        for(int y=1;y<=n;y++)
            if(idx[x][y]==1){
                if(idx[x][y-1]==0)ans++;
                if(idx[x][y+1]==0)ans++;
                if(idx[x+1][y]==0)ans++;
                if(idx[x-1][y]==0)ans++;
            }
    return ans;
}
int main()
{
    while(scanf("%d%d%d%d",&m,&n,&a,&b)&&m){
        memset(idx,0,sizeof(idx));
        memset(pic,'.',sizeof(pic));
        for(int i=1;i<=m;i++)
            scanf("%s",pic[i]+1);
        dfs(a,b);
        printf("%d\n",len());
    }
    return 0;
}

L - 搜索

dfs求联通块

#include <cstdio>
#include <string.h>
using namespace std;
char pic[100][100];
int m,n;
int idx[100][100];//表示(x,y)这个点属于第几连通块,等于0为未被探索
void dfs(int x,int y,int id)
{
    int dx,dy;
    if(x<0||y<0||x>=m||y>=n)return;//超出图的范围返回上一个点
    if(pic[x][y]!='@'||idx[x][y]>0)return;//不是油田或已被探索
    idx[x][y]=id;
 // printf("idx[%d][%d]=%d\n",x,y,id);
    for(dx=-1;dx<=1;dx++)
        for(dy=-1;dy<=1;dy++)
            if(dx!=0||dy!=0)dfs(x+dx,y+dy,id);//8方向探索
}
int main()
{
    while(scanf("%d%d",&m,&n)&&m&&n){
        memset(idx,0,sizeof(idx));
        for(int i=0;i<m;i++)
            scanf("%s",pic[i]);
        int cnt=0;
        for(int i=0;i<m;i++)
            for(int j=0;j<n;j++)
                if(idx[i][j]==0&&pic[i][j]=='@')//如果没被探索
                    dfs(i,j,++cnt);
        printf("%d\n",cnt);
    }
    return 0;
}

U - 搜索

dfs联通块

#include <cstdio>
#include <string.h>
#include <iostream>
using namespace std;
int cnt;
void dfs(char pic[23][23], int vis[23][23], int sx, int sy, int w, int h)
{
    int tx=sx,ty=sy;
    //printf("%d %d %c\n",ty,tx,pic[ty][tx]);
    if(sx<0||sx>=w||sy<0||sy>=h)
        return ;
    if(pic[sy][sx]=='#'||vis[sy][sx])
        return ;
    if(vis[ty][tx]==0){
        vis[ty][tx]=1;
        cnt++;
    }
    int i,j;
    for(i=-1;i<2;i++)
        for(j=-1;j<2;j++)
            if(!(i&j))
                dfs(pic,vis,tx+i,ty+j,w,h);
}
int main()
{
    int w,h,vis[23][23];
    char pic[23][23];
    while(scanf("%d%d",&w,&h)&&w){
        memset(vis,0,sizeof(vis));
        cnt=1;
        for(int i=0;i<h;i++)
            scanf("%s",pic[i]);
        int sx=-1,sy;
        for(int i=0;i<h;i++){
            if(sx!=-1)
                break;
            for(int j=0;j<w;j++)
                if(pic[i][j]=='@'){
                    sx=j,sy=i;
                    break;
                }
        }
        dfs(pic,vis,sx,sy,w,h);
        if(!cnt)
            printf("1\n");
        printf("%d\n",cnt-1);
    }

    return 0;
}

目录


from https://vjudge.net/contest/308052

A - 数字三角形

7
3 8
8 1 0
2 7 4 4
4 5 2 6 5

(Figure 1)
Figure 1 shows a number triangle. Write a program that calculates the highest sum of numbers passed on a route that starts at the top and ends somewhere on the base. Each step can go either diagonally down to the left or diagonally down to the right.
Input
Your program is to read from standard input. The first line contains one integer N: the number of rows in the triangle. The following N lines describe the data of the triangle. The number of rows in the triangle is > 1 but <= 100. The numbers in the triangle, all integers, are between 0 and 99.
Output
Your program is to write to standard output. The highest sum is written as an integer.
Sample Input
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
Sample Output
30

思路:dp[i][j]=max{ dp[i-1][j] , dp[i-1][j-1] } + v[i][j]
每行每列两端都扩充上0保证递推可用

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    int dp[110][110]={0},v[110][110]={0};
    int n;
    while(cin>>n){
        int i,j,ans=0;
        for(i=1;i<=n;i++){
            j=1;
            for(;j<=i;j++)
                cin>>v[i][j];
        }//stored
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+v[i][j];
                if(i==n&&dp[i][j]>ans)
                    ans=dp[i][j];
            }
        }
        cout<<ans<<endl;
    }
}

B - LITTLE SHOP OF FLOWERS

赋值0的时候多赋值到了dp[1][0]导致错误

#include <iostream>
#include <string.h>
#define INF 0x3f3f3f3f
using namespace std;
int main()
{
    int f,V,i,j;
    int v[101][101],dp[101][101];
    cin>>f>>V;
    for(i=1;i<=f;i++)
        for(j=1;j<=V;j++)
            cin>>v[i][j];
    memset(dp,-INF,sizeof(dp));
    for(int i=0;i<=100;i++)dp[0][i]=0;//i<=101
    //dp[i][j]表示前i个花插到前j个瓶子的最大价值
    //dp[i][j]=max(dp[i-1][j-1]+v[i][j],dp[i][j-1])  前者是这个花瓶插,后者是不插
    for(i=1;i<=f;i++)
        for(j=1;j<=V;j++)
            dp[i][j]=max(dp[i-1][j-1]+v[i][j],dp[i][j-1]);
    cout<<dp[f][V]<<endl;
    return 0;
}

C - 钉子和小球

从最高往下,遇到一个钉子下面的两个就概率分半,因为是分数,所以不是从1开始分1/2,而是从2^n开始分半。
一直WA是因为只用输入一组数据,然鹅写成了while(cin)
还有50层第一个是2^50int就会爆

#include <cstdio>
#include <string.h>
#include <iostream>
#define ll __int64
using namespace std;
ll pow(int t)
{
    ll a=1;
    for(int i=0;i<t;i++)
        a*=2;
    return a;
}
ll gcd(ll a,ll b)
{
    if(a<b){
        a^=b;
        b^=a;
        a^=b;
    }
    ll r=a%b;
    while(r){
        a=b;
        b=r;
        r=a%b;
    }
    return b;
}
int main()
{
    ll dp[55][55];
    char pic[55][55];
 // freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=i;j++)
            cin>>pic[i][j];
    memset(dp,0,sizeof(dp));
    dp[1][1]=pow(n);
    for(int i=1;i<=n+1;i++){
        for(int j=1;j<=i;j++){
            if(pic[i][j]=='*'){
                dp[i+1][j]+=dp[i][j]/2;
                dp[i+1][j+1]+=dp[i][j]/2;
            }
            else{
                dp[i+2][j+1]+=dp[i][j];
            }
        }
    }
    ll t=dp[n+1][m+1],g=gcd(t,dp[1][1]);
    if(t==0){
        printf("0/1\n");
    }
    printf("%lld/%lld\n",t/g,dp[1][1]/g);
    return 0;
}

D - 完全背包

多层背包,一年一背包就好
完全和01的区别是完全对于一物品可以选无数次

John never knew he had a grand-uncle, until he received the notary’s letter. He learned that his late grand-uncle had gathered a lot of money, somewhere in South-America, and that John was the only inheritor.
John did not need that much money for the moment. But he realized that it would be a good idea to store this capital in a safe place, and have it grow until he decided to retire. The bank convinced him that a certain kind of bond was interesting for him.
This kind of bond has a fixed value, and gives a fixed amount of yearly interest, payed to the owner at the end of each year. The bond has no fixed term. Bonds are available in different sizes. The larger ones usually give a better interest. Soon John realized that the optimal set of bonds to buy was not trivial to figure out. Moreover, after a few years his capital would have grown, and the schedule had to be re-evaluated.
Assume the following bonds are available:

ValueAnnual interest
4000400
3000250

With a capital of e10 000 one could buy two bonds of $4 000, giving a yearly interest of $800. Buying two bonds of $3 000, and one of $4 000 is a better idea, as it gives a yearly interest of $900. After two years the capital has grown to $11 800, and it makes sense to sell a $3 000 one and buy a $4 000 one, so the annual interest grows to $1 050. This is where this story grows unlikely: the bank does not charge for buying and selling bonds. Next year the total sum is $12 850, which allows for three times $4 000, giving a yearly interest of $1 200.
Here is your problem: given an amount to begin with, a number of years, and a set of bonds with their values and interests, find out how big the amount may grow in the given period, using the best schedule for buying and selling bonds.

Input
The first line contains a single positive integer N which is the number of test cases. The test cases follow.
The first line of a test case contains two positive integers: the amount to start with (at most $1 000 000), and the number of years the capital may grow (at most 40).
The following line contains a single number: the number d (1 <= d <= 10) of available bonds.
The next d lines each contain the description of a bond. The description of a bond consists of two positive integers: the value of the bond, and the yearly interest for that bond. The value of a bond is always a multiple of $1 000. The interest of a bond is never more than 10% of its value.

Output
For each test case, output – on a separate line – the capital at the end of the period, after an optimal schedule of buying and selling.

Sample Input
1
10000 4
2
4000 400
3000 250

Sample Output
14050

for (int i = 1;i <= N;i++)
{
	for (int v = weight[i];v <= V;v++)
	{
		f[v] = max(f[v],f[v - weight[i]] + Value[i]);
	}
}

原文:https://blog.csdn.net/insistGoGo/article/details/11081025
分析:这和01背包的伪代码很相似,在01背包的代码中,v变化的区间是逆序循环的,即[V,Weight[i]]。而这里,v变化的区间是顺序循环的,即为[Weight[i],V]。
原因:

再次给出定义:

f[i][v]表示把前i件物品放入容量为v的背包时的最大代价。

f[i-1][v-c[i]]表示把前i - 1件物品放入容量为v的背包时的最大代价.

在01背包中,v变化的区间是逆序循环的原因:要保证由状态f[i-1][v-c[i]]递推状态f[i][v]时,f[i-1][v-c[i]]没有放入第i件物品。之后,在第i循环时,放入一件第i件物品。

01背包的方程:

f[i][v] = max(f[i - 1][v],f[i - 1][v - weight[i]] + Value[i])
在完全背包中,v变化的区间是顺序循环的原因:完全背包的特点是每种物品可选无限件,在求解加选第i种物品带来的收益f[i][v]时,在状态f[i][v-c[i]]中已经尽可能多的放入物品i了,此时在f[i][v-c[i]]的基础上,我们可以再次放入一件物品i,此时也是在不超过背包容量的基础下,尽可能多的放入物品i。

完全背包的方程:

f[i][v] = max(f[i - 1][v],f[i][v - weight[i]] + Value[i]);
f[v]=max {f[v]*旧的* , f[v-weight[i]]*因为考虑的是i物品尽可能加进去了多个,所以必须是新的,比如已经加进去一个,这次考虑的是是否在加过的基础上(新的)再加* + value[i] }

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    int w[11],v[11];
    int T,ans,year,d,i,dp[100000];
    cin>>T;
    while(T--){
        cin>>ans>>year>>d;
        for(i=1;i<=d;i++)
            cin>>w[i]>>v[i];
        for(i=1;i<=d;i++)
            w[i]/=1000;
        while(year--){//一次完全背包
            int t=ans/1000;
            memset(dp,0,sizeof(dp));
            for(i=1;i<=d;i++)
                for(int j=w[i];j<=t;j++)
                    dp[j]=max(dp[j], dp[j-w[i]]+v[i]);
            ans+=dp[t];
        }
    cout<<ans<<endl;
    }
}

E - 01背包

分组背包和01背包差不多,只不过多加一个循环判断组内选哪个

Bessie has gone to the mall’s jewelry store and spies a charm bracelet. Of course, she’d like to fill it with the best charms possible from the N (1 ≤ N ≤ 3,402) available charms. Each charm i in the supplied list has a weight Wi (1 ≤ Wi ≤ 400), a ‘desirability’ factor Di (1 ≤ Di ≤ 100), and can be used at most once. Bessie can only support a charm bracelet whose weight is no more than M (1 ≤ M ≤ 12,880).

Given that weight limit as a constraint and a list of the charms with their weights and desirability rating, deduce the maximum possible sum of ratings.

Input
*Line 1: Two space-separated integers: N and M
*Lines 2…N+1: Line i+1 describes charm i with two space-separated integers: Wi and Di

Output
*Line 1: A single integer that is the greatest sum of charm desirabilities that can be achieved given the weight constraints

Sample Input
4 6
1 4
2 6
3 12
2 7
Sample Output
23

思路:用最低级的二维表格,自底向上求解
大神的详细描述
vi表示价值,wi表示重量,V(i,j)表示j容量下前i个物品可装的最高价值
对于第i个物品,可以选:最后的价值就是V(i-1 , j-wi)+vi
也可以不选:最后的价值就是V(i-1 , j)
但是对于wi>j的不可以选,因此主要逻辑是:

  1. wi>j : V(i,j)=V(i-1,j)
  2. wi <= j : V(i,j)=max { V(i-1,j) , V(i-1,j-wi)+vi}
  3. i=0时,表示没有东西可以装,V(0,j)=0
  4. j=0时,表示没空间,所以V(i,0)=0

因为每次都加了vi所以不用基例
对于V(x,y)需要一步步递推下去,所以我们制表从基层开始反向填
但是这样二维数组不够用啊,直接爆内存
使用static int范围不会炸
但是数据大了会WA,其实是因为没有考虑物品和背包无重量的情况
因此如果M是0,j就不可以从1取起,而应该是0
这次不会WA,但是RE了
因此要用滚动数组,但用的是2M空间
用一维数组是M空间

/*--------------------------WA||RE------------------------*/
#include <iostream>
#include <cmath>
#include <cstdio>
#include <string.h>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    static int w[3500],v[3500],dp[3500][13000];
    int N,M,i,j;
    while(cin>>N>>M){
        memset(dp,0,sizeof(dp));
        for(i=1;i<=N;i++)
            cin>>w[i]>>v[i];
        for(i=1;i<=N;i++){//前i个物品
            for(j=0;j<=M;j++){//j=1 wrong
                if(w[i]>j)
                    dp[i][j]=dp[i-1][j];//载重扩大的不够,还不可能存任何东西,与上一个载重一样
                else
                    dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
            }
        }
        cout<<dp[N][M]<<endl;//填表完成,最后一个就是答案了
    }
    return 0;
}
/*--------------------------WA||RE------------------------*/

滚动数组AC

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    static int w[3403],v[3403],dp[2][13000];
    int N,M,i,j;
    while(cin>>N>>M){
        memset(dp,0,sizeof(dp));
        for(i=1;i<=N;i++)
            cin>>w[i]>>v[i];
        for(i=1;i<=N;i++){//前i个物品
            for(j=0;j<=M;j++){//j=1 wrong
                if(w[i]>j)
                    dp[i%2][j]=dp[(i-1)%2][j];//载重扩大的不够,还不可能存任何东西,与上一个载重一样
                else
                    dp[i%2][j]=max(dp[(i-1)%2][j], dp[(i-1)%2][j-w[i]]+v[i]);
            }
        }
        cout<<dp[(i-1)%2][M]<<endl;//填表完成,最后一个就是答案了
    }
    return 0;
}

一位数组:突然明白

先考虑上面讲的基本思路如何实现,肯定是有一个主循环i=1…N,每次算出来二维数组f[i][0…V]的所有值。那么,如果只用一个数组f[0…V],能不能保证第i次循环结束后f[v]中表示的就是我们定义的状态f[i][v]呢?f[i][v]是由f[i-1][v]和f[i-1][v-c[i]]两个子问题递推而来,能否保证在推f[i][v]时(也即在第i次主循环中推f[v]时)能够得到f[i-1][v]和f[i-1][v-c[i]]的值呢?事实上,这要求在每次主循环中我们以v=V…0的顺序推f[v],这样才能保证推f[v]时f[v-c[i]]保存的是状态f[i-1][v-c[i]]的值。
原文:https://blog.csdn.net/tr990511/article/details/7595854

对于一维数组进行第i次循环开始时,数组保存的都是第i-1次的数据,因为状态方程要依靠的是i-1次的数据,如果从前往后,左边的数据会变成第i次的,而方程依靠的就是左边上一次的,所以只能从右边开始。

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    static int w[3403],v[3403],dp[13000];//13000是重量
    int N,M,i,j;
    while(cin>>N>>M){
        memset(dp,0,sizeof(dp));
        for(i=1;i<=N;i++)
            cin>>w[i]>>v[i];
        for(i=1;i<=N;i++)
            for(j=M;j>=w[i];j--)//这里直接判断了是否超重,如果i的重量大于j(剩余载重),价值就不可能会发生变化
                dp[j]=max(dp[j],dp[j-w[i]]+v[i]);
              //dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
        cout<<dp[M]<<endl;
    }
    return 0;
}

F - 最长公共子序列

A subsequence of a given sequence is the given sequence with some elements (possible none) left out. Given a sequence X = < x1, x2, …, xm > another sequence Z = < z1, z2, …, zk > is a subsequence of X if there exists a strictly increasing sequence < i1, i2, …, ik > of indices of X such that for all j = 1,2,…,k, x ij = zj. For example, Z = < a, b, f, c > is a subsequence of X = < a, b, c, f, b, c > with index sequence < 1, 2, 4, 6 >. Given two sequences X and Y the problem is to find the length of the maximum-length common subsequence of X and Y.
Input
The program input is from the std input. Each data set in the input contains two strings representing the given sequences. The sequences are separated by any number of white spaces. The input data are correct.
Output
For each set of data the program prints on the standard output the length of the maximum-length common subsequence from the beginning of a separate line.
Sample Input
abcfbc abfcab
programming contest
abcd mnp
Sample Output
4
2
0

设X=x1x2…xm和Y=y1y2…yn是两个序列,Z=z1z2…zk是这两个序列的一个最长公共子序列。
1.如果xm=yn,那么zk=xm=yn,且Zk-1是Xm-1,Yn-1的一个最长公共子序列;
2.如果xm≠yn,那么zk≠xm,意味着Z是Xm-1,Y的一个最长公共子序列;
3.如果xm≠yn,那么zk≠yn,意味着Z是X,Yn-1的一个最长公共子序列。
原文:https://blog.csdn.net/u013921430/article/details/79299678

xm=yn: lcm[m,n]=lcm[m-1,n-1]+1
otherwise: lcm[m,n]=max {lcm[m,n-1], lcm[m-1,n]}

#include <iostream>
#include <string>
#include <string.h>
#include <algorithm>
#include <cstdio>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    int lcm[1000][1000];
    string a,b;
    while(cin>>a>>b){
        memset(lcm,0,sizeof(lcm));
        int la=a.size(),lb=b.size();
        int i,j;
        for(i=1;i<=la;i++)
            for(j=1;j<=lb;j++){
                if(a[i-1]==b[j-1])
                    lcm[i][j]=lcm[i-1][j-1]+1;
                else
                    lcm[i][j]=max(lcm[i][j-1],lcm[i-1][j]);
            }
        cout<<lcm[la][lb]<<endl;
    }
    return 0;
}

G - 最长下降子序列

http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=29978&messageid=1&deep=0
最优问题dp,肯定优先考虑是否能累,因此按照长或宽排序。
dp[x]代表 拿前x个 拿到第x个能累多高,下面是方程(如果是前x个方程就是dp[i]由dp[i-1]推的了,明显不对,例如选了第i-1个,第i个的长宽和他一样,但明显更高,dp[i]仍然不会更新)。
dp[i]=max(dp[j]+block[i].h , dp[i]) 前者理解为在1–i-1中选一个再加自己,后者理解为前面有两个大小相同的,第一次dp加上了第一块,第二次dp用加上第2块的和加上第1块的进行比较。
每个方块都有6种情况,但只关心高的情况,可以假设长较长,因此只有三种情况了。
注意dp[3n]不一定是最大的。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cmath>
#include <cstdio>
using namespace std;
struct block
{
    int x,y,z;//x>y
};
int cmp(block a, block b)
{
    return a.x>b.x;
}
int main()
{
    block ls[100];
    int n,dp[100];
    int cnt=0;
    while(cin>>n&&n){
        memset(ls,0,sizeof(ls));
        int i,j;
        for(i=1;i<=n;i++){
            int a,b,c;
            cin>>a>>b>>c;
            ls[3*i-2].z=a,ls[3*i-2].x=max(b,c),ls[3*i-2].y=min(b,c);
            ls[3*i-1].z=b,ls[3*i-1].x=max(a,c),ls[3*i-1].y=min(a,c);
            ls[3*i].z=c,ls[3*i].x=max(b,a),ls[3*i].y=min(b,a);
        }
        sort(ls+1,ls+3*n+1,cmp);
        for(i=1;i<=3*n;i++)dp[i]=ls[i].z;
        for(i=1;i<=3*n;i++){
            for(j=1;j<i;j++){
                if(ls[j].x>ls[i].x&&ls[j].y>ls[i].y)
                    dp[i]=max(dp[j]+ls[i].z, dp[i]);
            }
        }
        int ans=0;
        for(int i=1;i<=3*n;i++)
            ans=max(ans , dp[i]);
        printf("Case %d: maximum height = %d\n",++cnt,ans);//dp[3*n]不一定是最大的啊
    }
    return 0;
}

H - 记忆化搜索

一开始在主函数里枚举一次,然而不行,对于12345/98765中的9状态应该是从右边8得到
因此要重新创个函数进行递归

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
using namespace std;
int vis[109][109];
int m,n;
int in(int x, int y)
{
    if(x>0&&x<m+1&&y>0&&y<m+1)
        return 1;
    return 0;
}
int dp(int pic[109][109], int i, int j, int v[109][109])
{
    if(vis[i][j]){//如果这个点已经被访问过了
        return v[i][j];
    }
    vis[i][j]=1;
    int now=pic[i][j],up=pic[i-1][j],right=pic[i][j+1],down=pic[i+1][j],left=pic[i][j-1];
    if(in(i-1,j)&&now>up){
        //cout<<"("<<i<<","<<j<<")"<<"up"<<endl;
        v[i][j]=max(v[i][j],dp(pic,i-1,j,v)+1);
    }
    if(in(i,j+1)&&now>right){
        //cout<<"("<<i<<","<<j<<")"<<"right"<<endl;
        v[i][j]=max(v[i][j],dp(pic,i,j+1,v)+1);
    }
    if(in(i+1,j)&&now>down){
        //cout<<"("<<i<<","<<j<<")"<<"down"<<endl;
        v[i][j]=max(v[i][j],dp(pic,i+1,j,v)+1);
    }
    if(in(i,j-1)&&now>left){
        //cout<<"("<<i<<","<<j<<")"<<"left"<<endl;
        v[i][j]=max(v[i][j],dp(pic,i,j-1,v)+1);
    }
    return v[i][j];
}
int main()
{
//    freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);
    int pic[109][109],v[109][109];
    memset(pic,0,sizeof(pic));
    memset(v,0,sizeof(v));
    cin>>m>>n;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
            cin>>pic[i][j];
    int ans=0;
    for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++){
            dp(pic,i,j,v);
            ans=max(ans,v[i][j]);
        }
    cout<<ans+1;
    return 0;
}

I - trie树

前缀树/字典树
树是从一个节点伸出来的,因此每次添加新节点的时候最好new一下
一开始把root写成了野指针,应该先把root设置为node,再用指针指向root

#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
    node *next[26];
    int val;
};
node root;//node *root返回错误,这是个野指针肯定用不成啊
void make(char *str)
{
    int len=strlen(str);
    int i;
    node *t=&root;
    for(i=0;i<len;i++){
        int id=str[i]-'a';
        if(t->next[id]==NULL){
            node *p = new node;//创建一个新node
            p->val=1;
            for(int i=0;i<26;i++)p->next[i]=NULL;
            t->next[id]=p;
        }
        else{
            t->next[id]->val++;
        }
        t=t->next[id];
    }
}
int find(char *str)
{
    int len=strlen(str),i,id;
    node *p=&root;
    for(i=0;i<len;i++){
        id=str[i]-'a';
        if(p->next[id]==NULL)
            return 0;
        p=p->next[id];
    }
    return p->val;
}
int main()
{
    for(int i=0;i<26;i++)root.next[i]=NULL;
    char str[30];
    while(1){
        gets(str);
        if(str[0]=='\0')
            break;
        make(str);
    }
    while(scanf("%s",str)!=EOF){
        printf("%d\n",find(str));
    }
    return 0;
}

J - trie树

next中只用两个指针
一开始root在main内定义,写函数的时候忘记加引用,一直不会修改root下的值

#include <cstdio>
#include <cstring>
using namespace std;
struct node
{
    node *next[2];
    int val;
    int flag;//表明是否是是最后一个字符
};
        /*else
        {
            int len=strlen(ls[i]);
            int j;
            node *t=&root;
            for(j=0;j<len;j++){
            int id=ls[i][j]-'0';
            if(t->next[id]==NULL){
                node *p = new node;//创建一个新node
                p->val=1,p->flag=0;
                for(int j=0;j<2;j++)p->next[j]=NULL;
                t->next[id]=p;
            }
            else{
                t->next[id]->val++;
            }
            t=t->next[id];
            printf("%d %d\n",t->val,t->flag);
    }
        t->flag++;
        }
        i++;
    }*/
void make(char *str,node &root)//void make(char *str,node root)
{
    int len=strlen(str);
    int i,j;
    node *t=&root;
    for(i=0;i<len;i++){
        int id=str[i]-'0';
        //printf("id is %d;",id);
        //printf("t->next[id]=NULL? is %d;",t->next[id]==NULL);
        if(t->next[id]==NULL){
            node *p = new node;//创建一个新node
            p->val=1,p->flag=0;
            for(j=0;j<2;j++)p->next[j]=NULL;
            t->next[id]=p;
        }
        else{
            //printf("do it;");
            t->next[id]->val++;
        }
        t=t->next[id];
        if(i==len-1)t->flag++;
        //printf("%d %d %d\n",i,t->val,t->flag);
    }
}
int find(char *str,node root)
{
   // printf("find:\n");
    int len=strlen(str),i,id;
   // printf("str:%s len:%d",str,len);
    node *p=&root;
    for(i=0;i<len;i++){
        //printf("    i:   %d\n",i);
        id=str[i]-'0';
        if(i<len&&p->flag>0){
            //printf("    i<len&&p->flag>0    :");
           // printf("p->flag:%d\n",p->flag);
            return 0;
        }
        if(i==len&&p->flag>1){
            //printf("    i==len&&p->flag>1    :");
            //printf("p->flag:%d\n",p->flag);
            return 0;
        }
        //printf("%d ",p->val);
        p=p->next[id];
    }
    return 1;
}
int main()
{
    node root;//node *root返回错误,这是个野指针肯定用不成啊
    root.val=999,root.flag=0;
    for(int i=0;i<2;i++)root.next[i]=NULL;
    char ls[10][30];
    int cnt=0,i=0;
    while(~scanf("%s",ls[i])){
        if(ls[i][0]=='9'){
            int j,ans=1;
            for(j=0;j<i;j++){
                ans = ans & find(ls[j],root);
            }
            if(ans)
                printf("Set %d is immediately decodable\n",++cnt);
            else
                printf("Set %d is not immediately decodable\n",++cnt);
            i=0;
            root.next[0]=NULL,root.next[1]=NULL;
            continue;
        }
        make(ls[i],root);
        //i++;
        /*else
            {
            char str[30];
            strcpy(str,ls[i]);
    int len=strlen(str);
    int i,j;
    node *t=&root;
    for(i=0;i<len;i++){
        int id=str[i]-'0';
        if(t->next[id]==NULL){
            node *p = new node;//创建一个新node
            p->val=1,p->flag=0;
            for(j=0;j<2;j++)p->next[j]=NULL;
            t->next[id]=p;
        }
        else{
            printf("%d  ",999);
            t->next[id]->val++;
        }
        t=t->next[id];
        if(i==len-1)t->flag++;
        printf("%d %d %d\n",i,t->val,t->flag);
    }
        }*/
        i++;
    }
    return 0;
}

K - 并查集

并查集数组实现看几个根节点
这里用join而不是pre[b]=a,因为遇到1 2,2 1这种环会不断更新
根节点选择自己的值就好,而不是-1

#include <cstdio>
#include <iostream>
#include <string.h>
using namespace std;
int find_root(int x, int parent[])
{
    int x_root=x;
    if(parent[x_root]==x)return x_root;
    else find_root(parent[x_root],parent);
}
int main()
{
    int parent[1009];
    int t,m=0;
    cin>>t;
    while(t--){
        m=0;
        int a,b,n;
        scanf("%d%d",&m,&n);
        for(int i=1;i<=m;i++)
            parent[i]=i;
        for(int i=0;i<n;i++){
            scanf("%d%d",&a,&b);
            int ra=find_root(a,parent),rb=find_root(b,parent);
            if(ra!=rb)parent[rb]=ra;
        }
        int cnt=0;
        for(int i=1;i<=m;i++){
            if(parent[i]==i)
                cnt++;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

L - 并查集 (find函数写法不同会WA?)

正常并查集,但是自己写的递归find_root有问题,很奇怪,K题这么写findroot都可以过的
递归的话5<-4<-3,parent[3]=4,parent[4]=5,但用循环都是5,有可能在递归的时候爆掉了

#include <iostream>
#include <string.h>
using namespace std;
int find_root(int x, int parent[]);
void join(int a, int b, int parent[])
{
    int ra=find_root(a,parent),rb=find_root(b,parent);
    if(ra!=rb){
        parent[ra]=rb;
    }
}

int find_root(int x,int pre[])
{
	int r=x;
	while(pre[r]!=r)
	r=pre[r];

	int i=x;
	int j;
	while(i!=j)
	{
		j=pre[i];
		pre[i]=r;
		i=j;
	}
	return r;

}

int find_roott(int x, int parent[])
{
    int x_root=parent[x];
    if(x==x_root)return x_root;
    else find_root(x_root,parent);
}
int main()
{
    int a[35000];
    int parent[35000];
    int n,m;
    while(cin>>n>>m&&n){
        if(m==0){
            cout<<1<<endl;
            continue;
        }
        for(int i=0;i<n;i++)
            parent[i]=i;
        for(int i=0;i<m;i++){
            int k;
            cin>>k;
            int first;
            for(int j=0;j<k;j++){
                cin>>a[j];
                join(a[0],a[j],parent);
            }
        }
        int ans=0;
        for(int i=0;i<n;i++){
            int t=find_root(i,parent);
            if(t==find_root(0,parent))
                ++ans;
        }
        cout<<ans<<endl;
    }
    return 0;
}

M - 并查集

第一次join写错了所以WA
主要思想是在修复好一个后全部尝试一遍,看已经修复的点能不能连在一起

#include <iostream>
#include <algorithm>
#include <cmath>
using namespace std;
int dx[1009],dy[1009],parent[1009],repair[1009];
double Dis(int a,int b)
{
    return sqrt( double(dx[a]-dx[b])*(dx[a]-dx[b]) + (dy[a]-dy[b])*(dy[a]-dy[b]) );
}
int find(int x)
{
    int r=x;
    while(r!=parent[r])
        r=parent[r];
    return r;
}
void join(int a, int b)
{
    int ra=find(a),rb=find(b);
    if(ra!=rb)
        parent[rb]=ra;//parent[b]=a;
}
int main()
{
    int n,d;
    cin>>n>>d;
    for(int i=1;i<=n;i++){
        parent[i]=i;
        cin>>dx[i]>>dy[i];
    }
    char c;
    int flag=1;//一个都没修
    int first;
    while(cin>>c){
        if(c=='O'){
            if(flag){
                cin>>first;
                flag=0;
                repair[first]=1;
                continue;
            }
            int a;
            cin>>a;
            repair[a]=1;
            for(int i=1;i<=n;i++)
                if(Dis(a,i)<=(double)d&&repair[i])
                    join(a,i);
        }
        if(c=='S'){
            int a,b;
            cin>>a>>b;
            if(find(a)==find(b))
                cout<<"SUCCESS"<<endl;
            else
                cout<<"FAIL"<<endl;
        }
    }
    return 0;
}

N - 并查集

和how many tables一样
一开始数组开小了RE

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdio>
using namespace std;
int parent[55000];
int find(int x)
{
    int r=x;
    while(r!=parent[r])
        r=parent[r];
    //优化
    int i,j;
    i=x,j=parent[i];
    while(i!=j){
        parent[i]=r;
        i=j;
        j=parent[i];
    }
    return r;
}
void join(int a, int b)
{
    int ra=find(a),rb=find(b);
    if(ra!=rb)
        parent[rb]=ra;//parent[b]=a;
}
int main()
{
    int n,m;
    int c=0;
    while(scanf("%d%d",&n,&m)&&n){
        for(int i=1;i<=n;i++)
            parent[i]=i;
        int a,b;
        for(int i=0;i<m;i++){
            scanf("%d%d",&a,&b);
            join(a,b);
        }
        int cnt=0;
        for(int i=1;i<=n;i++){
            if(parent[i]==i)
                cnt++;
        }
        printf("Case %d: %d\n",++c,cnt);
    }
    return 0;
}

P - Play on Words

欧拉回路,一笔画问题。
充分条件第一点就是连通。
对于无向图,除了起点和终点,其他点的度数应该为偶数。
对于有向图,最多两个点的入度不等于出度,而且一个的入度比出度大1(终点),另一个出度比入度大1(起点)。
对于 aa,bb,这种数据,并没有连通,但a,b的入度出度都是偶数,因此要用并查集来判断是否连通。
ans=0;
if(vis[i] && find(a)==a)表明有a这个点但是a的根仍是a,ans+++;
如果ans=1说明可能是欧拉回路ab,bc,ca;或欧拉通路ab,bc
如果ans>1说明不通aa,bb
ans会等于0吗?(自己觉得不可能)
如果每个点入度等于出度—>欧拉回路
只有两个点入度出度不等并且一个入度比另一个大1,他出度比另一个小一---->欧拉通路

#include <iostream>
#include <string>
#include <string.h>
using namespace std;
int find(int parent[], int x)
{
    if(x==parent[x])
        return x;
    find(parent,parent[x]);
}
void join(int parent[], int a, int b)
{
    int ra=find(parent,a),rb=find(parent,b);
    if(ra!=rb)
        parent[rb]=ra;
}
int main()
{
    int in[27],out[27],parent[27],vis[27];
    int T;
    cin>>T;
    while(T--){
        memset(in,0,sizeof(in));
        memset(out,0,sizeof(out));
        memset(vis,0,sizeof(vis));
        for(int i=1;i<=26;i++)parent[i]=i;
        int n;
        cin>>n;
        for(int i=1;i<=n;i++){
            string str;
            cin>>str;
            int a,b;
            a=str[0]-'a'+1;
            b=str[str.length()-1]-'a'+1;
            vis[a]=vis[b]=1;
            out[a]++;
            in[b]++;
            join(parent,a,b);
        }
        //接下来判读是否连通 条件1
        int cnt=0;
        for(int i=1;i<=26;i++){
            if(vis[i] && parent[i]==i){
                cnt++;
                if(cnt>1)break;
            }
        }
        if(cnt>1){
            cout<<"The door cannot be opened."<<endl;
            continue;
        }
        //下俩判断欧拉 条件2
        int pos[2],ans=0;
        for(int i=1;i<=26;i++){
            if(out[i]!=in[i]){
                pos[ans++]=i;
            }
        }
        if(ans==0)//欧拉回路
            cout<<"Ordering is possible."<<endl;
        else if(ans==2 && ((in[pos[0]]-out[pos[0]]==1 && out[pos[1]]-in[pos[1]]==1) || (out[pos[0]]-in[pos[0]]==1 && in[pos[1]]-out[pos[1]]==1)))
            cout<<"Ordering is possible."<<endl;
        else
            cout<<"The door cannot be opened."<<endl;
    }
    return 0;
}

A - 广搜

题意就是给你一个n,让你找一个只有0,1组成的n的倍数
一开始没有想懂为什么是bfs,试了一遍才知道
第一层是1,分为了10,11,再分为100,101,110,111
每次给每种情况加0/1
c++ TE g++ AC

#include <iostream>
#include <queue>
#define ll long long
using namespace std;
void bfs(int n)
{
    queue<ll> q;
    ll a=1;
    q.push(a);
    while(!q.empty()){
        a=q.front();
        q.pop();
        if(a%n==0){
            cout<<a<<endl;
            return ;
        }
        q.push(a*10);
        q.push(a*10+1);
    }
}
int main()
{
    int n;
    while(cin>>n&&n){
        bfs(n);
    }
    return 0;
}

G - 拓扑排序

拓扑主要是把入度为0的先排。
大意就是给你一个N个点的图,并且给你图中的有向边,要你输出一个可行的点拓扑序列即可.输入格式为,第一行点数N,以下接着有N行,每行以0结尾.第i行包含了以i点为起点的有向边所指的所有节点.。

//模板1
for( cnt = 0; cnt < |V|; cnt++ ){
	v = 未输出的入度为0的点;
	输出v;
	for( v 的每个邻接点 w )
		Indegree[w]--;
}
//模板2
for(每个顶点v)
	if(入度=0)
		入队
while(!empty()){
	v=出队
	for(v的每个邻接点)
		if(--入度=0)
			入队
	if(输出的不等于顶点的个数)
		图中有回路
}
#include <iostream>
#include <queue>
#include <string.h>
using namespace std;
void TopSort(int pic[][101], int Indegree[], int n)
{
    queue<int> q;
    for(int i=1;i<=n;i++)
        if(Indegree[i]==0){
            q.push(i);
        }
    while(!q.empty()){
        int v=q.front();
        q.pop();
        cout<<v<<" ";
        for(int j=1;j<=100;j++)
            if(pic[v][j]){
                pic[v][j]=0;
                if(--Indegree[j]==0)
                    q.push(j);
            }
    }
}
int main()
{
    int Indegree[101],pic[101][101];
    int n,a,i;
    memset(Indegree,0,sizeof(Indegree));
    memset(pic,0,sizeof(pic));
    cin>>n;
    for(i=1;i<=n;i++){
        while(cin>>a&&a){
            Indegree[a]++;
            pic[i][a]=1;
        }
    }
    TopSort(pic,Indegree,n);
    return 0;
}

H - 拓扑排序

要求了较小的排前,所以换做优先队列存储。
vector<int>,greater<int>。
还要注意到可能5战胜3有两次,不判断直接入度+1是错的,因为这样3的入度就成了2。
应该同时要判断5是否战胜过3。

#include <iostream>
#include <queue>
#include <string.h>
#include <functional>
using namespace std;
void TopSort(int pic[][501], int Indegree[], int n)
{
    int cnt=0;
    priority_queue<int,vector<int>,greater<int> > q;
    for(int i=1;i<=n;i++)
        if(Indegree[i]==0){
            q.push(i);
        }
    while(!q.empty()){
        int v=q.top();
        q.pop();
        cnt++;
        cout<<v;
        if(cnt<n)
            cout<<" ";
        for(int j=1;j<=n;j++)
            if(pic[v][j]){
                pic[v][j]=0;
                if(--Indegree[j]==0)
                    q.push(j);
            }
    }
    cout<<endl;
}
int main()
{
    int Indegree[501],pic[501][501];
    int n,m,a,b,i;
    while(cin>>n>>m){
        memset(Indegree,0,sizeof(Indegree));
        memset(pic,0,sizeof(pic));
        for(i=1;i<=m;i++){
            cin>>a>>b;
            if(!pic[a][b]){
                pic[a][b]=1;
                Indegree[b]++;
            }
        }
        TopSort(pic,Indegree,n);
    }
    return 0;
}

I - 线段树

add可以和sub写成一个
一开始数组开小导致RE
后来cin,cout取消同步还是TE
用scanf最后AC

#include <iostream>
#include <string.h>
#include <cstdio>
#include <string>
#define MAX 50005
using namespace std;

void Build(int arr[], int tree[], int node, int left, int right)//left是arr的,left_node是tree的,left和right分别是node的区间
{
    //printf("%d %d %d\n",node,left,right);
    if(left==right){
        tree[node]=arr[left];
        return ;
    }
    int mid=(left+right)/2;
    int left_node=2*node;
    int right_node=left_node+1;
    Build(arr,tree,left_node,left,mid);
    Build(arr,tree,right_node,mid+1,right);
    tree[node]=tree[left_node]+tree[right_node];
}

void Add(int arr[], int tree[], int node, int left, int right, int index, int val)
{
    if(left==right){
        tree[node]+=val;
        return ;
    }
    int mid=(left+right)/2;
    int left_node=2*node;
    int right_node=left_node+1;

    tree[node]+=val;
    if(index<=mid)
        Add(arr,tree,left_node,left,mid,index,val);
    else
        Add(arr,tree,right_node,mid+1,right,index,val);
}

int Query(int arr[], int tree[], int node, int left, int right, int L, int R)
{
    int mid=(left+right)/2;
    int left_node=2*node;
    int right_node=left_node+1;

    if(right<L||left>R)
        return 0;
    else if(left==right)
        return tree[node];
    else if(right<=R&&left>=L)
        return tree[node];

    int lv=Query(arr,tree,left_node,left,mid,L,R);
    int rv=Query(arr,tree,right_node,mid+1,right,L,R);
    return rv+lv;
}

int main()
{
    int Case=0;
    int T;
    cin>>T;
    while(T--){
        static int arr[MAX<<4],tree[MAX<<4];
        memset(arr,0,sizeof(arr));
        memset(tree,0,sizeof(tree));
        int N;
        scanf("%d",&N);//cin>>N;
        for(int i=1;i<=N;i++){
            scanf("%d",&arr[i]);
        }
        Build(arr,tree,1,1,N);
        char ins[20];
        printf("Case %d:\n",++Case);
        while(scanf("%s",ins)){//cin>>ins
            if(strcmp(ins,"Query")==0){
                int i,j,ans;
                scanf("%d%d",&i,&j);//cin>>i>>j;
                ans=Query(arr,tree,1,1,N,i,j);
                printf("%d\n",ans);//cout<<ans<<endl;
            }
            else if(strcmp(ins,"Add")==0){
                int i,j;
                scanf("%d%d",&i,&j);//cin>>i>>j;
                Add(arr,tree,1,1,N,i,j);
            }
            else if(strcmp(ins,"Sub")==0){
                int i,j;
                scanf("%d%d",&i,&j);//cin>>i>>j;
                Add(arr,tree,1,1,N,i,-j);
            }
            else{
                break;
            }
        }
    }
    return 0;
}

J - 线段树

等级是左下角的星星个数,因此计算x,y坐标都小于当前点的个数就可以。
同时输入是按y坐标升序,因此后一个y肯定大于等于前一个,二维压缩到一维,只用看x就可以。
原来的update是把原点改为x,这里是把原点改为+1。query-1表示的是当前的等级(1到x的数量-1),tree指的是坐标为[left,right]的个数,每次只改叶子节点。

#include <iostream>
#include <cstdio>
#include <string.h>
#define MAX 32005
using namespace std;
void join(int tree[], int node, int left, int right, int x)
{
    if(left==right){
        tree[node]++;
        return;
    }
    int mid=(left+right)>>1;
    if(x<=mid){
        join(tree,2*node,left,mid,x);
    }
    else{
        join(tree,2*node+1,mid+1,right,x);
    }
    tree[node]=tree[2*node]+tree[2*node+1];
}
int query(int tree[], int node, int left, int right, int from, int to)
{
    //cout<<left<<" "<<right<<endl;
    int mid=(left+right)>>1;
    if(from<=left&&to>=right)
        return tree[node];
    int ans=0;
    if(from<=mid)ans+=query(tree,2*node,left,mid,from,to);//在左子树有数据
    if(to>mid) ans+=query(tree,2*node+1,mid+1,right,from,to);
    return ans;
}
int main()
{
    static int tree[MAX<<2],ans[MAX];
    memset(tree,0,sizeof(tree));
    memset(ans,0,sizeof(ans));
    int N;
    scanf("%d",&N);
    int n=N;
    while(n--){
        getchar();
        int x,y;
        scanf("%d%d",&x,&y);
        join(tree,1,0,MAX,x);
        int t=query(tree,1,0,MAX,0,x)-1;//因为自己也算在个数内
        ans[t]++;
    }
    for(int i=0;i<N;i++)
        printf("%d\n",ans[i]);
    return 0;
}

K - 线段树 (最大最小值)

给两个索引,找到这两个索引见最大-最小。
只需要把线段树的存储内容变了就可以,把原来的区间和改为区间最大最小值。
这道题要对ans进行初始化,因为它可能只比较一边,另一边就是无穷了。

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
#include <cmath>
#define MAX 50005
using namespace std;
struct node
{
    int Max,Min;
};
void join(node tree[], int root, int left, int right, node p, int index)
{
    if(left==right){
        tree[root].Max=p.Max;
        tree[root].Min=p.Min;
        return;
    }
    int mid=(left+right)>>1;
    if(index<=mid){
        join(tree,2*root,left,mid,p,index);
    }
    else{
        join(tree,2*root+1,mid+1,right,p,index);
    }
    tree[root].Max=max(tree[2*root].Max,tree[2*root+1].Max);
    tree[root].Min=min(tree[2*root].Min,tree[2*root+1].Min);
}
node query(node tree[], int root, int left, int right, int from, int to)
{

    node ans,p;
    //cout<<left<<" "<<right<<"->";
    int mid=(left+right)>>1;
    if(from<=left&&to>=right)
        return tree[root];
    ans.Max=-1,ans.Min=1000009;
    if(from<=mid){
        p=query(tree,2*root,left,mid,from,to);//在左子树有数据
        ans.Max=max(ans.Max,p.Max);
        ans.Min=min(ans.Min,p.Min);
    }
    if(to>mid){
        p=query(tree,2*root+1,mid+1,right,from,to);
        ans.Max=max(ans.Max,p.Max);
        ans.Min=min(ans.Min,p.Min);
    }
    //printf("             p:(%d,%d)  q:(%d,%d)  \n",p.Max,p.Min,q.Max,q.Min);
    return ans;
}
int main()
{
    static node tree[MAX<<2];
    memset(tree,0,sizeof(tree));
    int N,Q;
    scanf("%d%d",&N,&Q);
    for(int i=1;i<=N;i++){
        getchar();
        int x;
        scanf("%d",&x);
        node p;
        p.Max=p.Min=x;
        join(tree,1,1,N,p,i);
    }
    getchar();
    int from,to;
    for(int j=0;j<Q;j++){
        scanf("%d%d",&from,&to);
        getchar();
        node t;
        t=query(tree,1,1,N,from,to);
        printf("%d\n",t.Max-t.Min);
    }
    return 0;
}

M - 线段树区间更改区间查询

相比于I题把int改longlong。
add函数增加时考虑区间有几个数属于i-j,直接加val*cnt。
直接从上而下更新会TE,要对add进行优化。

如果我们要让[ 8, 13 ]里面的都加上c ,那我们直接让 [ 8, 13 ] 这个节点加上 (13 - 8 + 1)*c ,不就得了嘛!
但是如果我们下面还要修改查询呢?子节点还是要修改的!但是我们万一不查询呢。所以这里利用这个特点,引入一个"延时标记"表示这个区间是要修改的,但是子节点还没修改。我们在查询或者修改时遇到这个标记就先更新他的子节点。但是我们在最初修改时只递归到这一层就可以返回了:
原文:https://blog.csdn.net/qq_40772692/article/details/81558535

#include <iostream>
#include <string.h>
#include <cstdio>
#include <string>
#define MAX 4000000
#define ll long long
using namespace std;
int Change[MAX];
void Build(ll arr[], ll tree[], ll node, ll left, ll right)//left是arr的,left_node是tree的,left和right分别是node的区间
{
    //printf("%d %d %d\n",node,left,right);
    if(left==right){
        tree[node]=arr[left];
        return ;
    }
    ll mid=(left+right)/2;
    ll left_node=2*node;
    ll right_node=left_node+1;
    Build(arr,tree,left_node,left,mid);
    Build(arr,tree,right_node,mid+1,right);
    tree[node]=tree[left_node]+tree[right_node];
}

void PushDown(ll arr[], ll tree[], ll node, ll left, ll right)
{
    if(Change[node]){
        ll mid=(left+right)/2;
        ll left_node=2*node;
        ll right_node=left_node+1;
        ll val=Change[node];

        Change[left_node]+=val;
        Change[right_node]+=val;
        tree[left_node]+=(mid-left+1)*val;
        tree[right_node]+=(right-mid)*val;
        Change[node]=0;
    }
}

void Add(ll arr[], ll tree[], ll node, ll left, ll right, ll from, ll to, ll val)
{
    if(from<=left&&to>=right){
        tree[node]+=(right-left+1)*val;
        Change[node]+=val;
        return ;
    }
    PushDown(arr,tree,node,left,right);
    //cout<<cnt<<endl;
    ll mid=(left+right)/2;
    ll left_node=2*node;
    ll right_node=left_node+1;
    if(from<=mid)Add(arr,tree,left_node,left,mid,from,to,val);
    if(to>mid)Add(arr,tree,right_node,mid+1,right,from,to,val);
    tree[node]=tree[left_node]+tree[right_node];
    return ;
}

ll Query(ll arr[], ll tree[], ll node, ll left, ll right, ll L, ll R)
{
    ll mid=(left+right)/2;
    ll left_node=2*node;
    ll right_node=left_node+1;

    if(right==R&&left==L)
        return tree[node];

    PushDown(arr,tree,node,left,right);
    if(R<=mid)return Query(arr,tree,left_node,left,mid,L,R);
    else if(L>mid) return Query(arr,tree,right_node,mid+1,right,L,R);
    else return Query(arr,tree,left_node,left,mid,L,mid)+Query(arr,tree,right_node,mid+1,right,mid+1,R);
}

int main()
{
    static ll arr[MAX],tree[MAX];
    memset(arr,0,sizeof(arr));
    memset(tree,0,sizeof(tree));
    ll N,Q;
    scanf("%lld%lld",&N,&Q);//cin>>N;
    for(ll i=1;i<=N;i++){
        scanf("%lld",&arr[i]);
    }
    Build(arr,tree,1,1,N);
    char ins;
    while(Q--){//cin>>ins
        getchar();
        scanf("%c",&ins);
        if(ins=='Q'){
            ll i,j,ans;
            scanf("%lld%lld",&i,&j);//cin>>i>>j;
            ans=Query(arr,tree,1,1,N,i,j);
            printf("%lld\n",ans);//cout<<ans<<endl;
        }
        else {
            ll i,j,k;
            scanf("%lld%lld%lld",&i,&j,&k);//cin>>i>>j;
            Add(arr,tree,1,1,N,i,j,k);
        }
    }

    return 0;
}

N - 最短路

题意:n个经理人,每个经理人认识其他的一些经理人,要交流情报,每个经理人只能与认识的人交流,并且交流需要时间,要求从那个经理人开始传情报能使最慢收到情报的经理人耗时最短,输出最短时间。
无源最短路,floyd算法。
最后扫描每一行,找到最大值后和前面几行比较,找出最小的,如果循环完后最小的仍然是INF,则说明路径不存在。

#include <iostream>
#include <algorithm>
#include <string.h>
#include <cstdio>
#define INF 0x3f3f3f3f
using namespace std;
int main()
{
    int pic[101][101];
    int n;
    while(cin>>n&&n){
        memset(pic,INF,sizeof(pic));
        for(int i=1;i<=n;i++)pic[i][i]=0;
        for(int from=1;from<=n;from++){
            int pairs;
            cin>>pairs;
            for(int p=1;p<=pairs;p++){
                int to,time;
                cin>>to>>time;
                pic[from][to]=time;
            }
        }

        int i,j,k;
        for(k=1;k<=n;k++){
            for(i=1;i<=n;i++){
                for(j=1;j<=n;j++){
                    pic[i][j]=min(pic[i][j],pic[i][k]+pic[k][j]);
                }
            }
        }

        int ans[2]={0,INF};
        for(int i=1;i<=n;i++){
            int *p=max_element(pic[i]+1,pic[i]+n+1);
            int M=*p;
            if(M<ans[1]){
                ans[0]=i;
                ans[1]=M;
            }
        }
        if(ans[1]==INF){
            printf("disjoint\n");
        }
        else{
            printf("%d %d\n",ans[0],ans[1]);
        }
    }
}

P - 最短路(最小生成树)

和Q没任何区别

#include <iostream>
#include <algorithm>
#include <cstdio>
#define ll long long
using namespace std;
struct edge
{
    ll from,to,dis;
};
ll find(ll parent[], ll root)
{
    if(parent[root]==root)
        return root;
    find(parent,parent[root]);
}
void join(ll parent[], ll a, ll b)
{
    ll ra,rb;
    ra=find(parent,a),rb=find(parent,b);
    if(ra!=rb)
        parent[rb]=ra;
}
ll cmp(edge a, edge b)
{
    return a.dis<b.dis;
}
int main()
{
    ll n;
    while(cin>>n&&n){
        ll parent[50000];
        edge edges[20000];
        char start;
        ll cases,num=0;
        for(ll nn=1;nn<=n-1;nn++){
            cin>>start>>cases;
            ll startid=start-'A'+1;
            for(ll cas=1;cas<=cases;cas++){
                ++num;
                char to;
                ll dis;
                cin>>to>>dis;
                ll toid=to-'A'+1;
                edges[num].from=startid;
                edges[num].to=toid;
                edges[num].dis=dis;
            }
        }
        for(ll i=1;i<=n;i++)parent[i]=i;//一开始自己是自己的父节点,全没连接
        sort(edges+1,edges+num+1,cmp);
        /*for(int i=1;i<=num;i++){
            printf("%lld %lld %lld\n",edges[i].from,edges[i].to,edges[i].dis);
        }
        cout<<"-----------------------"<<endl;*/
        ll cnt=0;
        ll ans=0;
        for(ll i=1;i<=num;i++){
            if(cnt==n-1)break;//最多n-1条边
            if(find(parent,edges[i].from)!=find(parent,edges[i].to)){//没有加入MST
                ans+=edges[i].dis;
                join(parent,edges[i].from,edges[i].to);//把两点放到一个集合
                cnt++;
                //printf("%lld %lld %lld\n",edges[i].from,edges[i].to,edges[i].dis);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

Q - 最小生成树

有坑点,a到b和b到a的距离不一定一样。
cpp WA , gpp AC。
一开始join忘记算a,b的根了。
kruskal模板

所有边从小到大排序
初始化MST为空
for(每个边){
	if(边上两点不在一个集合)//如果不构成环
		把这条边加入//并查集合并
}
#include <iostream>
#include <algorithm>
#include <cstdio>
#define ll long long
using namespace std;
struct edge
{
    ll from,to,dis;
};
ll find(ll parent[], ll root)
{
    if(parent[root]==root)
        return root;
    find(parent,parent[root]);
}
void join(ll parent[], ll a, ll b)
{
    ll ra,rb;
    ra=find(parent,a),rb=find(parent,b);
    if(ra!=rb)
        parent[rb]=ra;
}
ll cmp(edge a, edge b)
{
    return a.dis<b.dis;
}
int main()
{
    static ll n,pic[500][500];
    while(cin>>n){
        edge edges[20000];
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++)
                scanf("%lld",&pic[i][j]);
        ll num=n*(n-1),cnt=0;
        ll parent[20000];
        for(ll i=1;i<=n;i++)parent[i]=i;//一开始自己是自己的父节点,全没连接
        for(ll i=1;i<=n;i++)
            for(ll j=1;j<=n;j++){
                if(i==j)continue;
                ++cnt;
                edges[cnt].from=i;
                edges[cnt].to=j;
                edges[cnt].dis=pic[i][j];
            }
        sort(edges+1,edges+num+1,cmp);
        /*for(int i=1;i<=num;i++){
            printf("%lld %lld %lld\n",edges[i].from,edges[i].to,edges[i].dis);
        }
        cout<<"-----------------------"<<endl;*/
        cnt=0;
        ll ans=0;
        for(ll i=1;i<=num;i++){
            if(cnt==n-1)break;//最多n-1条边
            if(find(parent,edges[i].from)!=find(parent,edges[i].to)){//没有加入MST
                ans+=edges[i].dis;
                join(parent,edges[i].from,edges[i].to);//把两点放到一个集合
                cnt++;
                //printf("%lld %lld %lld\n",edges[i].from,edges[i].to,edges[i].dis);
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}

ACboy needs your help

分组背包

#include <iostream>
#include <cstdio>
#include <string.h>
#include <algorithm>
using namespace std;
int main()
{
 // freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    int v[110][110],dp[13000];//13000是重量
    int N,M,i,j;
    while(cin>>N>>M&&M!=0&&N!=0){
        memset(dp,0,sizeof(dp));
        for(i=1;i<=N;i++)
            for(j=1;j<=M;j++)
                cin>>v[i][j];
        for(i=1;i<=N;i++)
            for(j=M;j>=1;j--)
                for(int k=1;k<=j;k++)//k is w[i]
                    dp[j]=max(dp[j],dp[j-k]+v[i][k]);
                    //dp[i][j]=max(dp[i-1][j], dp[i-1][j-k]+v[i][k]);
        cout<<dp[M]<<endl;
    }
    return 0;
}

Bone Collector

背包

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <string.h>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    static int w[1003],v[1003],dp[1003][1003];
    int N,M,i,j,T;
    cin>>T;
    while(T--){
        cin>>N>>M;
        memset(dp,0,sizeof(dp));
        for(i=1;i<=N;i++)
            cin>>v[i];
        for(i=1;i<=N;i++)
            cin>>w[i];
        for(i=1;i<=N;i++){//前i个物品
            for(j=0;j<=M;j++){
                if(w[i]>j)
                    dp[i][j]=dp[i-1][j];//载重扩大的不够,还不可能存任何东西,与上一个载重一样
                else
                    dp[i][j]=max(dp[i-1][j], dp[i-1][j-w[i]]+v[i]);
            }
        }

/*          for (int i = 0; i < N+1; i++) {
            for (int j = 0; j < M+1; j++) {
                cout << dp[i][j] << ' ';
            }
            cout << endl;
        }
*/
        cout<<dp[N][M]<<endl;//填表完成,最后一个就是答案了
    }
    return 0;
}

最少拦截系统

每次都找和他差距最小的进行拦截

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Administrator\\Desktop\\input.txt","r",stdin);
    int def[20000],n,cnt,a,i,j;
    while(cin>>n){
        cnt=0;
        cin>>a;
        def[cnt++]=a;
        for(i=1;i<n;i++){
            cin>>a;
            for(j=0;j<cnt;j++)
                if(def[j]>=a){
                    def[j]=a;
                    break;
                }
            if(j==cnt)def[cnt++]=a;
            sort(def,def+cnt);
        }
        cout<<cnt<<endl;
    }
    return 0;
}

Frogger

floyd解决

//floyd 找的是各路径最大值中的最小值
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
pair<int,int> p[202];
double dis(pair<int,int> a, pair<int,int> b)
{
    return sqrt((double)(a.first-b.first)*(a.first-b.first)+(a.second-b.second)*(a.second-b.second));
}
int main()
{
    double pic[202][202];
    int t,cnt=1;
    while(scanf("%d",&t)&&t){
        printf("Scenario #%d\n",cnt++);
        for(int i=0;i<t;i++){
            scanf("%d%d",&p[i].first,&p[i].second);
        }
        for(int i=0;i<t;i++)
            for(int j=0;j<t;j++){
                if(i==j)pic[i][j]=0;
                else pic[i][j]=dis(p[i],p[j]);
            }
        //初始化完成
        for(int k=0;k<t;k++)
            for(int i=0;i<t;i++)
                for(int j=0;j<t;j++){
                    pic[i][j]=min(pic[i][j],max(pic[i][k],pic[k][j]));
                }
        printf("Frog Distance = %.3lf\n\n",pic[0][1]);
    }
    return 0;
}

Number Triangle

和前面不一样,这个范围大,用static存

#include <iostream>
#include <cmath>
#include <cstdio>
using namespace std;
int main()
{
//  freopen("C:\\Users\\Admin\\Desktop\\input.txt","r",stdin);
    static int dp[1100][1100]={0},v[1100][1100]={0};
    int n;
    while(cin>>n){
        int i,j,ans=0;
        for(i=1;i<=n;i++){
            j=1;
            for(;j<=i;j++)
                cin>>v[i][j];
        }//stored
        for(i=1;i<=n;i++){
            for(j=1;j<=i;j++){
                dp[i][j]=max(dp[i-1][j-1],dp[i-1][j])+v[i][j];
                if(i==n&&dp[i][j]>ans)
                    ans=dp[i][j];
            }
        }
        cout<<ans<<endl;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值