Codeforces Round 221 div2
转载请注明:http://www.cnblogs.com/dingxiaoxian/ 谢谢~
第一题:(水题,物理)
题意&解题思路:
给你个字符串,'^'表示支点,=表示竿子,'0'-'9'表示该位置挂着一个重量为对应数字的重物,问你天平是向左、向右、还是平衡?
第一次循环,找到支点并把每个位置物品重量求出;
第二次循环,求出每个点贡献的力矩;
最后比较左右边力矩和的大小得到答案。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <map> 6 using namespace std; 7 #define ONLINE_JUDGE 8 long long num[1123456]; 9 int main () 10 { 11 12 #ifndef ONLINE_JUDGE 13 freopen ( "test.in", "r", stdin ); 14 #endif 15 string tp; 16 while(cin>>tp) 17 { 18 long long zd=0,l=0,r=0; 19 int len=tp.size(); 20 memset(num,0,sizeof(num)); 21 for(int i=0;i<len;i++) 22 if(tp[i]=='^')zd=i;//找到支点 23 else if(tp[i]<='9'&&tp[i]>='0')num[i]=tp[i]-'0';//记录该位置物品重量 24 for(int i=0;i<len;i++) 25 if(i<zd)l+=(num[i]*abs(zd-i));//将当前力矩加入左边 26 else r+=(num[i]*abs(zd-i));//将当前力矩加入右边 27 if(l==r) cout<<"balance"<<endl; 28 else if(l<r)cout<<"right"<<endl; 29 else cout<<"left"<<endl; 30 } 31 return 0; 32 }
第二题:(图,简单逻辑)
题意&解题思路:
有n个人,它们间有m种关系,给出每个关系的三个数字a,b,v表示a欠b价值v的债,如果a欠b价值v,b欠c价值v则可转移为a欠c价值v的债务,这样总的债务总量由2v变成了v,给你所有的债务关系,问你总的债务总量最小为多少?
相当于n个点有m个有向边的图,每个有向边就表示a在向b放水(水可能是这点产生的,也可以是其他点流过来再流出去的),统计每个点的入流量和出流量,入流量小于出流量的点就表明多出的这部分流量是由该点产生的,而不是其他点发出来经过这里的。我们将所有的由自己产生的水求和,就能得到最小的债务总量了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <map> 6 using namespace std; 7 #define ONLINE_JUDGE 8 int point[111]; 9 int main () 10 { 11 12 #ifndef ONLINE_JUDGE 13 freopen ( "test.in", "r", stdin ); 14 #endif 15 int n,m; 16 while(cin>>n>>m) 17 { 18 memset(point,0,sizeof(point)); 19 int a,b,c; 20 for(int i=0;i<m;i++)cin>>a>>b>>c,point[a]+=c,point[b]-=c;//将起点加上c的数量,终点减去c的数量 21 int ans=0; 22 for(int i=0;i<=n;i++)if(point[i]>0)ans+=point[i];//统计所有出流量大于入流量的点的流量差 23 cout<<ans<<endl;//输入流量差之和 24 } 25 return 0; 26 }
第三题:(数论,打表,找规律)
题意&解题思路:
感谢kuangbin学长的指导!
这题给你一个长度大于等于4位,小于1e6的数字,其中没有前导零,且一定包含(1,6,8,9),问你在将这个数字数位进行一定调换后能否找出一个不含前导零的数字使其能被7整除,若有则输出其中一个,没有就输出0
在邝神指导下,我们发现(1,6,8,9)四个数字可以有4!种组合方法,其中可以涵盖%7的所有取值可能,这里我们用其中一组M[7]={1869,6189,1689,6891,1698,1986,8196}依次为%7为0-6的排列方法,那我们可以在题目给的数字中先将(1,6,8,9)四个 数字各抽出一个放在最前面组成一个数m,剩下n-4个数字组成一个数x,那我们构造的这个数字就可以用m*10^(n-4)+x表示,这时我们对这个数%7,设j=(10^(n-4))%7,k=x%7
则我们这个数字%7等价于 (m*j+k)%7,(j、k都是定值),可知,m可以是M数组中的任意数我们设取m=M[i];因为M[i]%7==i所以我们这个数字%7等价于 (i*j+k)%7(0<=i,j,k<7)这里的*可以看为模7的乘法,用群和有限域的一些知识我们可以知道i取每个值时这个式子都对应不同的答案(其实也可以笔上算算看,反正肯定不会重复的~~)这时我们就能保证这个式子能有一种情况的i使答案为0,那这时的M[i]*10^(n-4)+x就是我们要求的数字了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <map> 6 using namespace std; 7 #define ONLINE_JUDGE 8 int mod10(int k)//求10^k%7 9 { 10 int ans=1; 11 while(k--)ans=(ans*10)%7; 12 return ans; 13 } 14 int modS(string s,int k)//求k位长的数字S%7 15 { 16 int ans=0,i=0; 17 while(i<k)ans=(ans*10+s[i++]-'0')%7; 18 return ans; 19 } 20 int mod(int n,string s) 21 { 22 for(int i=0,j=mod10(n),k=modS(s,n);i<7;i++) 23 if((i*j+k)%7==0)return i;//循环找到使数字模7为0的i时返回 24 } 25 int M[7]={1869,6189,1689,6891,1698,1986,8196};//构建由1,6,8,9组成数字模7为0-6的数组 26 int main () 27 { 28 #ifndef ONLINE_JUDGE 29 freopen ( "test.in", "r", stdin ); 30 #endif 31 string S; 32 while(cin>>S) 33 { 34 int n=S.length(); 35 int mark=15,i=0; 36 while(mark!=0)//mark为0时表示1,6,8,9都找到并从字符串中删除了 37 if(S[i]=='1'&&(mark&1))(mark-=1),S=S.substr(0,i)+S.substr(i+1,n--);//删除当前字符并修改标记同时将字符串长度减1 38 else if(S[i]=='6'&&(mark&2))(mark-=2),S=S.substr(0,i)+S.substr(i+1,n--);//同上 39 else if(S[i]=='8'&&(mark&4))(mark-=4),S=S.substr(0,i)+S.substr(i+1,n--);//同上 40 else if(S[i]=='9'&&(mark&8))(mark-=8),S=S.substr(0,i)+S.substr(i+1,n--);//同上 41 else i++;//没找到以上数字,匹配下一位数字 42 cout<<M[mod(n,S)]<<S<<endl;//输出M[i]*10^(n-4)+S 43 } 44 return 0; 45 }
第四题:(dp,状态要合理)
题意&解题思路:
一个仅由0和1构成的n*m的矩阵,行可以任意交换,问你能产生最大的元素为全1的子矩阵的面积是多少?
方法比较暴力,但是比小伙伴们需要排序的要好,不过写的时候用cin结果不小心超时了= =
对每一行做简单的dp,dp[i]的值就是以i位置结尾的最长的连续1的串的长度;
构造一个M的二维数组,M[i][j]表示在整个矩阵中以i位置结尾的最大连续1串长度为j的行有多少行。
可以在每行dp时将M[i][dp[i]]的值+1就能得到整个M数组;
则我们可以知道若我们要求以第i列为右边界,长为j的全1子矩阵最大的面积
就是j*∑M[i][k](k>=j);
两重循环枚举所有情况最大值就能得到答案.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <map> 6 using namespace std; 7 #define ONLINE_JUDGE 8 9 int M[5555][5555]; 10 bool Map[5555][5555]; 11 int main () 12 { 13 14 #ifndef ONLINE_JUDGE 15 freopen ( "test.in", "r", stdin ); 16 #endif 17 int n,m; 18 while(scanf("%d%d",&n,&m)!=EOF) 19 { 20 char ch[5555]; 21 memset(M,0,sizeof(M)); 22 for(int i=0;i<n;i++){ 23 scanf("%s",ch);//之前 用cin超时,很尴尬= = 24 for(int j=0;j<m;j++) 25 Map[i][j]=(ch[j]=='1');//将数字存入Map 26 } 27 int dp[5555]; 28 for(int i=0;i<n;i++) 29 { 30 dp[0]=(Map[i][0]?1:0),M[0][dp[0]]++;//对0位置初始化 31 for(int j=1;j<m;j++) 32 dp[j]=(Map[i][j]?dp[j-1]+1:0),M[j][dp[j]]++; 33 //将每个位置的dp值求出并对相应的M元素加上1,表示这种状态的行又多了一个 34 } 35 int ans=0; 36 for(int i=0;i<m;i++){ 37 int minx=0; 38 for(int j=m;j>0;j--) 39 ans=max(ans,(M[i][j]+minx)*j),minx+=M[i][j]; 40 //从最大向最小算,用中间变量减少了一重累加的过程 41 //minx即表示以i位置结尾连续1串长度比j大的行有多少个 42 } 43 cout<<ans<<endl; 44 } 45 return 0; 46 }
第五题:
英语渣啊看不懂~~~~求大神讲解啦~~~~~~